Czy anonimowa metoda W C# może wywołać się sama?

Mam następujący kod:

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;

                };
   ......
   }
}

Czy jest inny sposób na skonfigurowanie anonimowej metody w C#, tak aby mogła ona wywoływać się sama?

Author: Matt, 2009-07-30

6 answers

Możesz podzielić ją na dwa wyrażenia i użyć magii przechwyconych zmiennych, aby uzyskać efekt rekurencji:

myDelegate build = null;
build = delegate(Object bj)
        {
           var letters= string.Empty;
           if (someCondition)
               return build(some_obj);                            
           else string.Empty;
        };
 94
Author: mmx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-30 19:17:07

Jeśli tworzysz funkcję rekurencyjną, zalecam unikanie anonimowych delegatów. Wystarczy utworzyć metodę i wywołać ją rekurencyjnie.

Metody anonimowe mają być anonimowe - nie powinieneś wywoływać ich po imieniu (nie-anonimowo).

 30
Author: Reed Copsey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-30 19:16:35

Anonymous Recursion in C # ma wspaniałą dyskusję na ten temat.

Rekurencja jest piękna, a lambdy są ostateczna abstrakcja. Ale jak można mogą być używane razem? Lambdy to funkcje anonimowe i rekurencja wymaga nazwisk...

Ponieważ to pojawiło się ponownie, oto przykład użycia kombinatora Y:

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

Oto jego użycie do wywołania anonimowej, rekurencyjnej funkcji ...

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

Zauważysz że jeśli nie użyjesz y-combinatora i skonfigurujesz rekurencję tylko za pomocą delegata, nie otrzymasz poprawnej rekurencji. Na przykład ...

// This is BAD. Do not do this!
Func<int,int> badRec = null;
badRec = x => ( x <= 1 ) ? 1 : x * badRec( x - 1 );
Ale wszystko działa dobrze ...
Console.WriteLine( badRec(5) );

// Output
// 120
Ale spróbuj tego ...
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
Co?!?

Widzisz, po linii badRec = x => x + 1;, delegat, który masz, jest taki ...

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

BadRec zwiększa wartość o 1, czego oczekujemy (4+1=5), ale badRecCopy zwraca teraz kwadrat wartości (5*( (5+1)-1 ) czego prawie na pewno się nie spodziewaliśmy.

Jeśli użyjesz y-combinatora, będzie działał zgodnie z oczekiwaniami ...

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

I dostajesz to, czego oczekujesz.

goodRec = x => x + 1;

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

// Output
// 5
// 120

Możesz przeczytać więcej o Y-combinator (link PDF).

 25
Author: JP Alioto,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-05 17:01:12

Nie można wywołać build wewnątrz samej build, ponieważ ciało anonimowej metody jest inicjalizacją samej zmiennej. Próbujesz użyć zmiennej przed jej zdefiniowaniem.

Nie polecam tego (ponieważ byłoby to dużo prostsze stworzyć tutaj prawdziwą metodę, która jest rekurencyjna) , ale jeśli jesteś zainteresowany, możesz przeczytać anonimową rekurencję w C#:

Rekurencja jest piękna, a lambdy są ostateczna abstrakcja. Ale jak można oni być używane razem? Lambdy to funkcje anonimowe i rekurencja wymaga nazwisk.

 10
Author: Andrew Hare,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-30 19:16:56

Jeśli użyjesz Y , twoja funkcja stanie się parametrem samej funkcji, więc możesz ją wywołać rekurencyjnie:

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);
  }
}
 3
Author: Jordão,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-10-05 17:48:33

Jeśli docierasz do punktu rekurencyjnych metod anonimowych, możesz promować je jako normalną, prywatną metodę w swojej klasie.

 1
Author: Will Eddins,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-07-30 19:17:22