4
votes

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.

2
Why is the method called SquareAllTheNumbers but prints the square root, instead of the square of every number?MindSwipe
Seems that this question is a duplicate of the next one: Passing a single item as IEnumerable<T>. For the last ten years a situation with passing a single value as IEnumerable hasn't changed much.Iliar Turdushev
The edit to your question would be an excellent answer to Passing a single item as IEnumerable<T>. You might add the SingleSequence<T> from this answer to your tests as well.dbc
I'll second that request to see this added as answer to the original question. Would also like to see this experiment repeated with newer framework versions, 3.1, 5, 6, see if there has been any change.Marc L.

2 Answers

1
votes

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);
}
0
votes

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)
}