2009-07-30 9 views
44

Ich habe den folgenden Code:Kann eine anonyme Methode in C# selbst nennen?

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; 

       }; 
    ...... 
    } 
} 

Gibt es eine andere Möglichkeit, um eine anonyme Methode in C# einzurichten, so dass sie selbst anrufen können?

+0

Die genaue Beschwerde von VS2008 ist: Lokale Variable ‚build‘ vor initialisiert möglicherweise nicht zugreifen. – Matt

Antwort

78

Sie können es in zwei Anweisungen brechen und die Magie der erfassten Variablen verwenden, um die Rekursion Effekt zu erzielen:

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

+1 Schön gemacht! Sehr clevere Lösung :) –

+12

Richtig;) Aber die resultierende Funktion ist nicht anonym - in Ihrem Ansatz ist der Rekursionstrick * noch * nur durch eine Namensbindung möglich. – Dario

29

Wenn Sie eine rekursive Funktion erstellen, würde ich empfehlen, anonyme Teilnehmer zu vermeiden. Erstellen Sie einfach eine Methode und lassen Sie sie sich rekursiv aufrufen.

Anonyme Methoden sollen anonym sein - man sollte sie nicht mit Namen (nicht anonym) anrufen.

+3

+1 Ich könnte mehr nicht zustimmen. –

+0

Dies ist immer noch anonym genug. – Lodewijk

+0

Rekursive Funktion ist irgendwie einfacher zu lesen als oben benannter Delegierter. Auch delegate selbst muss definiert werden, das ist auch minus für diese Lösung und plus für dieses. – TarmoPikaro

10

Sie können nicht build innerhalb build selbst aufrufen, da der Körper der anonymen Methode die Initialisierung der Variablen selbst ist. Sie versuchen, eine Variable zu verwenden, bevor sie definiert ist.

Nicht, dass ich empfehlen diese (wie es hier viel einfacher erstellen eine echte Methode wäre, die rekursiv ist), aber wenn Sie daran interessiert sind, können Sie lesen Anonymous Recursion in C#:

Rekursion ist schön und Lambda-Ausdrücke sind die ultimative Abstraktion. Aber wie können sie zusammen verwendet werden? Lambda-Ausdrücke sind anonyme Funktionen und Rekursion erfordert Namen.

+0

+1 für die gute Beschreibung, warum der Fehler existiert. Es ist leicht zu umgehen (siehe Mehrdads Antwort), aber ich denke nicht, dass es eine gute Idee ist. –

1

Wenn Sie auf den Punkt der rekursive anonyme Methoden, wenn es darum, Sie möchten um es zu einer normalen, privaten Methode in Ihrer Klasse zu machen.

23

Anonymous Recursion in C# hat eine tolle Diskussion zu diesem Thema.

Rekursion ist schön und lambdas sind die ultimative Abstraktion. Aber wie kann sie zusammen verwendet werden? Lambda-Ausdrücke sind anonyme Funktionen und Rekursion erfordert Namen ...

Da dieser wieder aufgetaucht, hier ist ein Beispiel für die Y-Combinator der Verwendung:

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

Hier ist eine Nutzung der es sich um eine nennen anonym, rekursive Funktion ...

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

Sie werden feststellen, dass, wenn Sie und die Rekursion nur mit dem Delegierten setzen nicht die Y-Combinator aufbrauchen, bekommt man nicht corre ct Rekursion. Zum Beispiel ...

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

Aber alles funktioniert ...

Console.WriteLine(badRec(5)); 

// Output 
// 120 

Aber versuchen Sie diese ...

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 

Was?!?

Sie sehen, nach der Zeile badRec = x => x + 1;, der Delegierte Sie tatsächlich haben dies ...

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

So badRec den Wert um 1 erhöht wird, die wir (4+1=5) erwarten, aber badRecCopy nun eigentlich die Rückkehr Quadrat des Wertes (5*((5+1)-1) was wir fast sicher nicht erwartet haben.

Wenn Sie die Y-Combinator verwenden, wird es wie erwartet ...

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

Und Sie bekommen, was Sie erwarten.

goodRec = x => x + 1; 

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

// Output 
// 5 
// 120 

Sie können mehr über die Y-combinator (PDF-Link) lesen.

+4

Y-Kombinator für den Sieg! :-) –

3

Wenn Sie Y verwenden, wird Ihre Funktion ein Parameter an die Funktion selbst, so dass Sie es rekursiv anrufen:

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