Is there a way to call a C# method requiring an IEnumerable<T> with a single value? …with benchmarking [duplicate]

亡梦爱人 提交于 2020-06-26 12:24:27

问题


I'm just wondering if I'm missing out on some syntactic sugar or micro-optimization... If I'm calling a method requiring an IEnumerable of values but I only have a single value, then I put that single value in an array. for example:

var x = 1.23;
SquareAllTheNumbers(new[] { x });


private void SquareAllTheNumbers(IEnumerable<double> values)
{
    foreach (var value in values)
    {
        Console.WriteLine(Math.Sqrt(value));
    }
}

This requires one to make an array even though one is not needed. It would seem that I should be able to write something like:

var x = 1.23;
SquareAllTheNumbers(yield x);

Is there a way to call the function without making an array?

[EDIT] Thanks for the quick responses. So, what I've learned is that there are four solutions to this as pointed out below and at the past identical question (Passing a single item as IEnumerable<T>). I sure like the extra yield method, and that wouldn't require generating a new array, but is it better? Well, I've been meaning to learn more about Benchmark.NET, so I decided to test these four ideas. These are:

  1. make an array (new[] { x } )
  2. yield method
  3. Enumerable.Repeat
  4. params double[]

These can be better understood in the code below:

        [Benchmark(Description = "Array Approach")]
        public double ArrayApproach()
        {
            var x = 1.23;
            return SumOfSquaredNumbers(new[] { x });
        }

        [Benchmark(Description = "Yield Method Approach")]
        public double YieldMethodApproach()
        {
            return SumOfSquaredNumbers(Yield(x));
        }

        [Benchmark(Description = "Enumerable Repeat Approach")]
        public double RepeatMethodApproach()
        {
            return SumOfSquaredNumbers(Enumerable.Repeat(x, 1));
        }
        [Benchmark(Description = "Params Approach")]
        public double ParamsApproach()
        {
            return SumOfSquaredNumbers(x);
        }

        private double SumOfSquaredNumbers(IEnumerable<double> values)
        {
            var result = 0.0;
            foreach (var value in values)
            {
                result = value * value;
            }

            return result;
        }
        private double SumOfSquaredNumbers(params double[] values)
        {
            var result = 0.0;
            foreach (var value in values)
            {
                result = value * value;
            }

            return result;
        }

        public IEnumerable<T> Yield<T>(T item)
        {
            yield return item;
        }

Here is the result from benchmarking:

|                       Method |      Mean |      Error |    StdDev |  Gen 0 | Allocated |
|----------------------------- |----------:|-----------:|----------:|-------:|----------:|
|             'Array Approach' |  6.970 ns | 10.7471 ns | 0.5891 ns | 0.0076 |      32 B |
|      'Yield Method Approach' | 30.256 ns | 21.7358 ns | 1.1914 ns | 0.0114 |      48 B |
| 'Enumerable Repeat Approach' | 30.058 ns | 32.9930 ns | 1.8085 ns | 0.0095 |      40 B |
|            'Params Approach' |  5.330 ns |  0.6169 ns | 0.0338 ns | 0.0077 |      32 B |

So, it looks like the best approach is to just make a new array. The fourth option (use params double[]) performs well but it is a bit outside of the question, since I meant the method to be "set in stone" - for example, part of a 3rd party DLL.


回答1:


An array is a concrete implementation of an IEnumerable, so I see nothing wrong in creating one to pass a value.

If you are after syntactic sugar, you could have an overload with a params argument, which means that the compiler will take care of creating an array for you:

private static void SquareAllTheNumbers(params double[] values) {
    foreach (var value in values) {
        Console.WriteLine(Math.Sqrt(value));
    }
}

static void Main(string[] args) {
    SquareAllTheNumbers(2);
    SquareAllTheNumbers(1,2,3);
}



回答2:


Not as far as I'm aware no, there isn't. Instead you should think about making one method that accepts a single parameter and another method that takes an IEnumerable of the same parameter. E.G

// May need to be renamed, as it implies this method returns value^2 but actually returns value^(1/2)
private double SquareNumber(double value)
{
    return Math.Sqrt(value);
}

private IEnumerable<double> SquareAllTheNumbers(IEnumerable<double> values)
{
    foreach (var value in values)
       yield return Math.Sqrt(value)
}


来源:https://stackoverflow.com/questions/62296111/is-there-a-way-to-call-a-c-sharp-method-requiring-an-ienumerablet-with-a-singl

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!