Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to yield return inside anonymous methods?

Basically I have an anonymous method that I use for my BackgroundWorker:

worker.DoWork += ( sender, e ) =>
{
    foreach ( var effect in GlobalGraph.Effects )
    {
        // Returns EffectResult
        yield return image.Apply (effect);
    }
};

When I do this the compiler tells me:

"The yield statement cannot be used inside an anonymous method or lambda expression"

So in this case, what's the most elegant way to do this? Btw this DoWork method is inside a static method, in case that matters for the solution.

like image 402
Joan Venge Avatar asked Mar 23 '11 21:03

Joan Venge


People also ask

Can an anonymous function return a value?

An anonymous method can return a value. The value is returned by use of the return statement. The type of the return value must be compatible with the return type specified by the delegate. In this version, the value of sum is returned by the code block that is associated with the count delegate instance.

When using yield return in a method What should the method's return type be?

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.

What is yield return?

The yield is the income the investment returns over time, typically expressed as a percentage, while the return is the amount that was gained or lost on an investment over time, usually expressed as a dollar value.

Does yield break return null?

"yield break" breaks the Coroutine (it's similar as "return"). "yield return null" means that Unity will wait the next frame to finish the current scope. "yield return new" is similar to "yield return null" but this is used to call another coroutine.


3 Answers

Unfortunately you can't.

The compiler does not allow you to combine the two "magic" pieces of code. Both involve rewriting your code to support what you want to do:

  1. An anonymous method is done by moving the code to a proper method, and lifting local variables to fields on the class with that method
  2. An iterator method is rewritten as a state machine

You can, however, rewrite the code to return the collection, so in your particular case I would do this:

worker.DoWork += ( sender, e ) => {     return GlobalGraph.Effects         .Select(effect => image.Apply(effect)); }; 

though it looks odd for an event (sender, e) to return anything at all. Are you sure you're showing a real scenario for us?


Edit Ok, I think I see what you're trying to do here.

You have a static method call, and then you want to execute code in the background, and then return data from that static method once the background call completes.

This is, while possible, not a good solution since you're effectively pausing one thread to wait for another, that was started directly before you paused the thread. In other words, all you're doing is adding overhead of context switching.

Instead you need to just kick off the background work, and then when that work is completed, process the resulting data.

like image 70
Lasse V. Karlsen Avatar answered Sep 21 '22 14:09

Lasse V. Karlsen


Perhaps just return the linq expression and defer execution like yield:

return GlobalGraph.Effects.Select(x => image.Apply(x));
like image 37
Tejs Avatar answered Sep 22 '22 14:09

Tejs


Unless I'm missing something, you can't do what you're asking.

(I do have an answer for you, so please read past my explanation of why you can't do what you're doing first, and then read on.)

You full method would look something like this:

public static IEnumerable<EffectResult> GetSomeValues()
{
    // code to set up worker etc
    worker.DoWork += ( sender, e ) =>
    {
        foreach ( var effect in GlobalGraph.Effects )
        {
            // Returns EffectResult
            yield return image.Apply (effect);
        }
    };
}

If we assume that your code was "legal" then when GetSomeValues is called, even though the DoWork handler is added to worker, the lambda expression isn't executed until the DoWork event is fired. So the call to GetSomeValues completes without returning any results and the lamdba may or may not get called at a later stage - which is then too late for the caller of the GetSomeValues method anyway.

Your best answer is to the use Rx.

Rx turns IEnumerable<T> on its head. Instead of requesting values from an enumerable, Rx has values pushed to you from an IObservable<T>.

Since you're using a background worker and responding to an event you are effectively having the values pushed to you already. With Rx it becomes easy to do what you're trying to do.

You have a couple of options. Probably the simplest is to do this:

public static IObservable<IEnumerable<EffectResult>> GetSomeValues()
{
    // code to set up worker etc
    return from e in Observable.FromEvent<DoWorkEventArgs>(worker, "DoWork")
           select (
               from effect in GlobalGraph.Effects
               select image.Apply(effect)
           );
}

Now callers of your GetSomeValues method would do this:

GetSomeValues().Subscribe(ers =>
{
    foreach (var er in ers)
    {
        // process each er
    }
});

If you know that DoWork is only going to fire once, then this approach might be a little better:

public static IObservable<EffectResult> GetSomeValues()
{
    // code to set up worker etc
    return Observable
        .FromEvent<DoWorkEventArgs>(worker, "DoWork")
        .Take(1)
        .Select(effect => from effect in GlobalGraph.Effects.ToObservable()
                          select image.Apply(effect))
        .Switch();  
}

This code looks a little more complicated, but it just turns a single do work event into a stream of EffectResult objects.

Then the calling code looks like this:

GetSomeValues().Subscribe(er =>
{
    // process each er
});

Rx can even be used to replace the background worker. This might be the best option for you:

public static IObservable<EffectResult> GetSomeValues()
{
    // set up code etc
    return Observable
        .Start(() => from effect in GlobalGraph.Effects.ToObservable()
                     select image.Apply(effect), Scheduler.ThreadPool)
        .Switch();  
}

The calling code is the same as the previous example. The Scheduler.ThreadPool tells Rx how to "schedule" the processing of subscriptions to the observer.

I hope this helps.

like image 43
Enigmativity Avatar answered Sep 25 '22 14:09

Enigmativity