2009-07-30 8 views
44

tengo el siguiente código:¿Puede llamarse un método anónimo en C#?

class myClass 
{ 
private delegate string myDelegate(Object bj); 

protected void method() 
    { 
    myDelegate build = delegate(Object bj) 
       { 
        var letters= string.Empty; 
        if (someCondition) 
         return build(some_obj); //This line seems to choke the compiler 
        else string.Empty; 

       }; 
    ...... 
    } 
} 

¿Hay otra manera de establecer un método anónimo en C# tal que puede llamarse a sí misma?

+0

La queja exacta de VS2008 es: variable local 'construir' no puede ser inicializado antes accediendo – Matt

Respuesta

78

Puede dividirlo en dos declaraciones y el uso de la magia de las variables capturadas para lograr el efecto recursividad:

myDelegate build = null; 
build = delegate(Object bj) 
     { 
      var letters= string.Empty; 
      if (someCondition) 
       return build(some_obj);        
      else string.Empty; 
     }; 
+4

+1 ¡Bien hecho! Solución muy ingeniosa :) –

+12

Verdadero;) Pero la función resultante no es anónima: en su enfoque, el truco de recursión * todavía * solo es posible mediante un enlace de nombre. – Dario

29

Si está creando una función recursiva, recomendaría evitar a los delegados anónimos. Simplemente crea un método y haz que se llame recursivamente.

Los métodos anónimos están destinados a ser anónimos; no debe llamarlos por su nombre (de forma no anónima).

+3

+1 No podría estar más de acuerdo. –

+0

Este/yo todavía es lo suficientemente anónimo. – Lodewijk

+0

La función recursiva es de alguna manera más fácil de leer que el delegado nombrado anteriormente. También se debe definir el propio delegado, que también es negativo para esa solución y más para este. – TarmoPikaro

10

No se puede llamar al build en el interior del build ya que el cuerpo del método anónimo es la inicialización de la variable en sí. Está tratando de usar una variable antes de que esté definida.

No es que me recomiendan este (ya que sería mucho más simple para crear un método real aquí que es recursivo), pero si usted está interesado puede leer Anonymous Recursion in C#:

La recursividad es hermoso y lambdas está la máxima abstracción. Pero, ¿cómo pueden usarse juntos? Lambdas son funciones anónimas y recursión requiere nombres.

+0

+1 para la buena descripción de por qué existe el error. Es fácil solucionarlo (ver la respuesta de Mehrdad), pero no creo que sea una buena idea en primer lugar. –

1

Si está llegando al punto de métodos anónimos recursivas, es posible que desee para promocionarlo como un método privado y normal en su clase.

23

Anonymous Recursion in C# tiene una excelente discusión sobre este tema.

Recursión es hermosa y lambdas son la máxima abstracción. Pero, ¿cómo pueden usarse juntos? Lambdas son funciones anónimas y recursividad requiere nombres ...

Dado que este apareció de nuevo, aquí hay un ejemplo del uso de la Y-Combinator:

// This is the combinator 
public static Func<A,R> Y<A,R>(Func<Func<A,R>, Func<A,R>> f) 
{ 
    Func<A,R> g = null; 
    g = f(a => g(a)); 
    return g; 
} 

aquí hay un uso de él para llamar a una anónima, función recursiva ...

Func<int,int> exp = Y<int,int>(e => x => (x <=1) ? 1 : x * e(x - 1)); 
Console.WriteLine(exp(5)); 

se dará cuenta de que si no se utiliza la y-Combinator y configura la recursividad con sólo el delegado, usted no consigue corre ct recursión. Por ejemplo ...

// This is BAD. Do not do this! 
Func<int,int> badRec = null; 
badRec = x => (x <= 1) ? 1 : x * badRec(x - 1); 

Pero todo funciona bien ...

Console.WriteLine(badRec(5)); 

// Output 
// 120 

Pero prueba este ...

Func<int,int> badRec = null; 
badRec = x => (x <= 1) ? 1 : x * badRec(x - 1); 

Func<int,int> badRecCopy = badRec; 

badRec = x => x + 1; 

Console.WriteLine(badRec(4)); 
Console.WriteLine(badRecCopy(5)); 

// Output 
// 5 
// 25 

¿Qué?!?

Ves, después de la línea badRec = x => x + 1;, el delegado que tiene actualmente es esto ...

badRecCopy = x => (x <= 1) ? 1 : x * ((x+1)-1); 

Así, badRec está incrementando el valor en 1 que esperamos (4+1=5), pero badRecCopy está ahora en realidad la devolución de los cuadrado del valor (5*((5+1)-1) que casi con seguridad no esperábamos.

Si utiliza la Y-Combinator, que funcionará como se espera ...

Func<int,int> goodRec = Y<int,int>(exp => x => (x <=1) ? 1 : x * exp(x - 1)); 
Func<int,int> goodRecCopy = goodRec; 

y se obtiene lo que espera.

goodRec = x => x + 1; 

Console.WriteLine(goodRec(4)); 
Console.WriteLine(goodRecCopy(5)); 

// Output 
// 5 
// 120 

Puede leer más sobre el Y-combinator (PDF Link).

+4

Y-combinator para la victoria! :-) –

3

Si utiliza Y, su función se convierte en un parámetro a la función en sí, por lo que se le puede llamar de forma recursiva:

class myClass { 
    private delegate string myDelegate(Object bj); 
    protected void method() { 
    myDelegate build = delegate(Object obj) { 
     // f is the function itself, which is passed into the function 
     return Functional.Y<Object, string>(f => bj => { 
     var letters = string.Empty; 
     if (someCondition) 
      return f(some_obj); // use f 
     else return string.Empty; 

     })(obj); 
    }; 
    } 
} 

public static class Functional { 
    public delegate Func<A, R> Recursive<A, R>(Recursive<A, R> r); 
    public static Func<A, R> Y<A, R>(Func<Func<A, R>, Func<A, R>> f) { 
    Recursive<A, R> rec = r => a => f(r(r))(a); 
    return rec(rec); 
    } 
} 
Cuestiones relacionadas