tipos metodos funciones expresiones ejemplos delegados anonimos anonimas c# .net backgroundworker anonymous-methods yield-return

c# - metodos - ¿Cómo rendir retorno dentro de métodos anónimos?



metodos anonimos c# (7)

A menos que me esté perdiendo algo, no puedes hacer lo que estás pidiendo.

(Tengo una respuesta para usted, así que lea la explicación de por qué no puede hacer lo que está haciendo primero y luego siga leyendo).

Tu método completo se vería algo así:

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

Si asumimos que su código era "legal", cuando se llama a GetSomeValues , a pesar de que el controlador DoWork se agrega al worker , la expresión lambda no se ejecuta hasta que se DoWork evento DoWork . Así que la llamada a GetSomeValues completa sin devolver ningún resultado y la lamdba puede o no ser llamada en una etapa posterior, lo cual es demasiado tarde para la persona que llama con el método GetSomeValues .

Su mejor respuesta es al uso de Rx .

Rx gira IEnumerable<T> sobre su cabeza. En lugar de solicitar valores de un enumerable, Rx tiene valores IObservable<T> desde un IObservable<T> .

Ya que está usando un trabajador de fondo y respondiendo a un evento, efectivamente ya se le han enviado los valores. Con Rx se vuelve fácil hacer lo que estás tratando de hacer.

Tienes unas cuantas opciones. Probablemente lo más simple es hacer esto:

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

Ahora los llamadores de su método GetSomeValues harían esto:

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

Si sabe que DoWork solo se disparará una vez, este enfoque podría ser un poco mejor:

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

Este código parece un poco más complicado, pero solo convierte un evento do work en una secuencia de objetos EffectResult .

Entonces el código de llamada se ve así:

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

Rx puede incluso ser usado para reemplazar al trabajador de fondo. Esta podría ser la mejor opción para usted:

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

El código de llamada es el mismo que en el ejemplo anterior. Scheduler.ThreadPool le dice a Rx cómo "programar" el procesamiento de las suscripciones al observador.

Espero que esto ayude.

Básicamente tengo un método anónimo que uso para mi BackgroundWorker :

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

Cuando hago esto el compilador me dice:

"La declaración de rendimiento no se puede utilizar dentro de un método anónimo o una expresión lambda"

Entonces, en este caso, ¿cuál es la forma más elegante de hacer esto? Por cierto, este método DoWork está dentro de un método estático, en caso de que sea importante para la solución.


Desafortunadamente no puedes.

El compilador no le permite combinar las dos piezas "mágicas" de código. Ambos implican volver a escribir su código para apoyar lo que quiere hacer:

  1. Un método anónimo se realiza moviendo el código a un método adecuado y elevando las variables locales a los campos de la clase con ese método
  2. Un método iterador se reescribe como una máquina de estado

Sin embargo, puede volver a escribir el código para devolver la colección, por lo que en su caso particular, haría esto:

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

aunque parece extraño que un evento (sender, e) devuelva algo en absoluto. ¿Estás seguro de que estás mostrando un escenario real para nosotros?

Editar Ok, creo que veo lo que estás tratando de hacer aquí.

Tiene una llamada de método estático, y luego desea ejecutar el código en segundo plano y luego devolver los datos de ese método estático una vez que la llamada de fondo finalice.

Esto es, mientras sea posible, no es una buena solución ya que efectivamente está deteniendo un subproceso para esperar a otro, que se inició directamente antes de pausar el subproceso. En otras palabras, todo lo que está haciendo es agregar sobrecarga de cambio de contexto.

En su lugar, simplemente debe iniciar el trabajo en segundo plano, y luego, cuando ese trabajo se haya completado, procesar los datos resultantes.


El trabajador debe establecer la propiedad Resultado de DoWorkEventArgs.

worker.DoWork += (s, e) => e.Result = GlobalGraph.Effects.Select(x => image.Apply(x));


Ok, hice algo como esto que hace lo que quería (algunas variables se omiten):

public static void Run ( Action<float, EffectResult> action ) { worker.DoWork += ( sender, e ) => { foreach ( var effect in GlobalGraph.Effects ) { var result = image.Apply (effect); action (100 * ( index / count ), result ); } } };

y luego en el sitio de la llamada:

GlobalGraph.Run ( ( p, r ) => { this.Progress = p; this.EffectResults.Add ( r ); } );


Para los nuevos lectores: la forma más elegante de implementar ''iteradores anónimos'' (es decir, anidados en otros métodos) en C # 5 es probablemente algo así como este truco genial con async / await (no se confunda con estas palabras clave, el código a continuación es computado de forma absolutamente sincrónica - ver detalles en la página enlazada):

public IEnumerable<int> Numbers() { return EnumeratorMonad.Build<int>(async Yield => { await Yield(11); await Yield(22); await Yield(33); }); } [Microsoft.VisualStudio.TestTools.UnitTesting.TestMethod] public void TestEnum() { var v = Numbers(); var e = v.GetEnumerator(); int[] expected = { 11, 22, 33 }; Numbers().Should().ContainInOrder(expected); }

C # 7 (disponible ahora en Visual Studio 15 Preview) admite funciones locales, que permiten el yield return :

public IEnumerable<T> Filter<T>(IEnumerable<T> source, Func<T, bool> filter) { if (source == null) throw new ArgumentNullException(nameof(source)); if (filter == null) throw new ArgumentNullException(nameof(filter)); return Iterator(); IEnumerable<T> Iterator() { foreach (var element in source) { if (filter(element)) { yield return element; } } } }


Tal vez solo devuelva la expresión linq y aplace la ejecución como rendimiento:

return GlobalGraph.Effects.Select(x => image.Apply(x));


DoWork es del tipo DoWorkEventHandler que no devuelve nada ( void ), por lo que no es posible en absoluto en su caso.