Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error 'Iterator cannot contain return statement ' when calling a method that returns using a yield

I'm hoping there's a nicer way to write this method & overloads with less code duplication. I want to return a sequence of deltas between items in a list. this method:-

    public static IEnumerable<decimal> CalculateDeltas(this IEnumerable<decimal> sequence)
    {
        decimal prev = default(decimal);
        foreach (var item in sequence)
        {
            var current = item;
            decimal diff = current - prev;
            prev = item;
            yield return diff;
        }
    }

works just fine.

I then thought about an overload which would allow an absolute delta, but would call the original method if absolute wasn't required:-

    public static IEnumerable<decimal> CalculateDeltas(this IEnumerable<decimal> sequence,bool absolute)
    {
        if (absolute)
        {
            decimal prev = default(decimal);
            foreach (var item in sequence)
            {
                var current = item;
                decimal diff = Math.Abs(current - prev);
                prev = item;
                yield return diff;
            }
        }
        else
        {
            return CalculateDeltas(sequence);
        }
    }

but this doesn't compile because of Error

"Cannot return a value from an iterator. Use the yield return statement to return a value, or yield break to end the iteration."

I've had a look at this post and it seems like I won't be able to do anything other than repeating the code from the original method:-

    public static IEnumerable<decimal> CalculateDeltas(this IEnumerable<decimal> sequence,bool absolute)
    {
        if (absolute)
        {
            decimal prev = default(decimal);
            foreach (var item in sequence)
            {
                var current = item;
                decimal diff = Math.Abs(current - prev);
                prev = item;
                yield return diff;
            }
        }
        else
        {
            decimal prev = default(decimal);
            foreach (var item in sequence)
            {
                var current = item;
                decimal diff = current - prev;
                prev = item;
                yield return diff;
            }
        }
    }

Can anyone suggest a better way of doing this?

like image 681
Dave00Galloway Avatar asked Apr 30 '14 14:04

Dave00Galloway


People also ask

Is IEnumerable an iterator?

The IEnumerable type is just an interface that allows you to iterate over some elements.

What is yield break?

It specifies that an iterator has come to an end. You can think of yield break as a return statement which does not return a value. For example, if you define a function as an iterator, the body of the function may look like this: for (int i = 0; i < 5; i++) { yield return i; } Console.

How does yield return work C#?

The yield return statement returns one element at a time. The return type of yield keyword is either IEnumerable or IEnumerator . The yield break statement is used to end the iteration. We can consume the iterator method that contains a yield return statement either by using foreach loop or LINQ query.


2 Answers

A single method cannot both yield return and return. You must choose one or the other.

You can either do a foreach to yield return the list:

else
{
    foreach (var item in CalculateDeltas(sequence))
        yield return item;
}

Or separate your code into two methods:

if (absolute)
    return CalculateAbsoluteDeltas(sequence);
else
    return CalculateDeltas(sequence);
like image 170
Tim S. Avatar answered Sep 22 '22 05:09

Tim S.


The simplest approach is probably to split the method into two, one of which is implemented via an iterator block and one of which isn't:

public static IEnumerable<decimal> CalculateDeltas(this IEnumerable<decimal> sequence,
                                                   bool absolute)
{
    return absolute ? CalculateAbsoluteDeltas(sequence) 
                    : CalculateDeltas(sequence);
}

private static IEnumerable<decimal> CalculateAbsoluteDeltas
    (IEnumerable<decimal> sequence)
{
    decimal prev = default(decimal);
    foreach (var item in sequence)
    {
        var current = item;
        decimal diff = Math.Abs(current - prev);
        prev = item;
        yield return diff;
    }
}

This split approach also allows you to eagerly validate sequence, e.g.

if (sequence == null) {
    throw new ArgumentNullException("sequence");
}

... in the non-iterator block method.

like image 41
Jon Skeet Avatar answered Sep 21 '22 05:09

Jon Skeet