2009-07-30 5 views
44

У меня есть следующий код:Может ли анонимный метод называть 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 является: Local «сборки» Переменная не может быть инициализирована до получающий доступ. – Matt

ответ

78

Вы можете разбить его на два заявления и использовать магию захваченных переменных для достижения эффекта рекурсии:

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

Это/я все еще достаточно анонимный. – Lodewijk

+0

Рекурсивная функция как-то проще читать, чем названный делегат выше. Также необходимо определить делегат, что также является минусом для этого решения и плюс для этого. – TarmoPikaro

10

Вы не можете позвонить build внутри build, так как тело анонимного метода является инициализацией самой переменной. Вы пытаетесь использовать переменную до ее определения.

Не то, что я рекомендую это (как это было бы гораздо проще создать реальный метод здесь, что является рекурсивным), но если вы заинтересованы вы можете прочитать Anonymous Recursion in C#:

Рекурсия красиво и лямбды являются конечная абстракция. Но как можно использовать ? Lambdas анонимные функции и рекурсия требуют имен.

+0

+1 для хорошего описания причины возникновения ошибки. Легко обходиться (см. Ответ Мехрдада), но я не считаю его хорошей идеей в первую очередь. –

1

Если вы получаете до точки рекурсивных анонимных методов, вы можете чтобы он стал обычным, частным методом в вашем классе.

23

Anonymous Recursion in C# имеет потрясающее обсуждение на эту тему.

Рекурсия прекрасна и лямбда Конечная абстракция. Но как можно использовать ? Лямбда являются анонимными функциями и рекурсия требует имен ...

Поскольку это выскочило снова, вот пример использования 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; 
} 

Вот это использование его для называют анонимный, рекурсивная функция ...

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

вы заметите, что если вы не используете Y-комбинатор и настроить рекурсию только с делегатом, вы не получите Corre ct рекурсия. Например ...

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

Но все работает отлично ...

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 является приращением значения на 1, который мы ожидаем (4+1=5), но 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 Link).

+4

Y-combinator для победы! :-) –

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); 
    } 
} 
Смежные вопросы