2009-07-30 22 views
44

I持って次のコード: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; 

       }; 
    ...... 
    } 
} 

それは自分自身を呼び出すことができるように、C#で匿名メソッドを設定するための別の方法はありますか?

+0

VS2008からの正確な苦情がある:ローカル変数「ビルド」を前に初期化されない場合がありますアクセスする。 – Matt

答えて

78

次の2つの文にそれを打破し、再帰効果を達成するために撮影した変数の魔法を使用することができます:あなたが再帰匿名メソッドのポイントに取得している

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

+1うまくやった!非常に巧妙な解決:) –

+12

True;)しかし、結果の関数は匿名ではありません - あなたのアプローチでは、再帰トリック*まだ*名前のバインディングを介して可能です。 – Dario

29

再帰関数を作成する場合は、匿名の代理人を避けることをおすすめします。メソッドを作成し、それ自体を再帰的に呼び出すようにしてください。

匿名メソッドは匿名であるため、名前で呼び出すことはできません(匿名ではありません)。

+3

+1私はもっと同意できませんでした。 –

+0

この/ selfはまだ匿名です。 – Lodewijk

+0

再帰関数は、上記の名前付き代理人よりも読みやすいです。デリゲート自体も定義する必要があります。これは、そのソリューションのマイナスでもあり、これに対してプラスです。 – TarmoPikaro

10

buildbuildの内部では呼び出せません。匿名メソッドの本体は変数自体の初期化であるためです。変数が定義される前にそれを使用しようとしています。私はこれをお勧めします(再帰的である、ここで実際のメソッドを作成するためにずっと単純なように)しかし、あなたが興味を持っている場合は、Anonymous Recursion in C#を読むことができることを

ない:

再帰が美しく、ラムダがあります 究極の抽象化。しかし、どうすればそれらを一緒に使うことができますか? ? Lambdaは 匿名関数と再帰 には名前が必要です。

+0

+1なぜエラーが存在するのかをよく理解しています。回避策は簡単です(Mehrdadの答えを参照)。しかし、私は最初にそれを良いアイデアとは考えていません。 –

1

場合は、必要ありそれをあなたのクラスの通常のプライベートメソッドにするように促します。

23

Anonymous Recursion in C#このトピックに関する素晴らしい議論があります。

再帰が美しく、ラムダは であり、究極の抽象化である。しかし、どうすればそれらを一緒に使うことができますか? ?ここで

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

呼び出すためのそれの使用だ:これが再び現れたので、ここでYコンビネータを使った例だ、...

ラムダは 匿名関数であり、再帰 には名前が必要です匿名の、再帰関数...

Func<int,int> exp = Y<int,int>(e => x => (x <=1) ? 1 : x * e(x - 1)); 
Console.WriteLine(exp(5)); 
あなたはYコンビネータを使用して、ちょうどデリゲートで再帰を設定しない場合、あなたはcorreを取得しないことに注意します

ct再帰。たとえば...

​​

しかし、すべてが正常に動作します...

Console.WriteLine(badRec(5)); 

// Output 
// 120 

しかし、これを試してみてください...

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 

何ですか?ラインbadRec = x => x + 1;後、あなたが実際に持っているデリゲートがこれです

あなたが見

、...

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

ので、badRecは、私たちが(4+1=5)を期待している1で値をインクリメントされますが、badRecCopyは今、実際に戻っていますほぼ確実に期待していなかった値(5*((5+1)-1)の正方形。

あなたはYコンビネータを使用する場合は、予想通り、それは動作します...

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

そして、あなたは、あなたが期待するものを手に入れます。

goodRec = x => x + 1; 

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

// Output 
// 5 
// 120 

あなたはY-combinator(PDFリンク)についての詳細を読むことができます。

+4

勝利のためのYコンビネータ! :-) –

3

あなたがYを使用している場合は、再帰的にそれを呼び出すことができるように、あなたの関数は、関数自体にパラメータとなる:

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); 
    } 
} 
関連する問題