我有以下代码:
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 :(得分:89)
您可以将其分解为两个语句,并使用捕获变量的魔力来实现递归效果:
myDelegate build = null;
build = delegate(Object bj)
{
var letters= string.Empty;
if (someCondition)
return build(some_obj);
else string.Empty;
};
答案 1 :(得分:30)
如果您正在创建递归函数,我建议您避免使用匿名代理。只需创建一个方法并让它以递归方式调用它。
匿名方法是匿名的 - 您不应该通过名称(非匿名)调用它们。
答案 2 :(得分:24)
Anonymous Recursion in C#对此主题进行了极好的讨论。
递归是美丽的,lambdas是 最终的抽象。但是怎么可能 他们一起使用?兰巴达是 匿名函数和递归 需要名字......
由于再次弹出,这是使用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;
}
以下是用来调用匿名递归函数的用法......
Func<int,int> exp = Y<int,int>( e => x => ( x <=1 ) ? 1 : x * e( x - 1 ) );
Console.WriteLine( exp(5) );
您将注意到,如果您不使用Y-combinator并仅使用委托设置递归,则无法获得正确的递归。例如......
// 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-combinator,它将按预期工作......
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链接)的更多信息。
答案 3 :(得分:10)
您无法在build
内部调用build
,因为匿名方法的主体是变量本身的初始化。您正在尝试在定义之前使用变量。
不是我推荐这个(因为在这里创建一个递归的真实方法会更简单 更简单)但是如果你有兴趣可以阅读Anonymous Recursion in C#:
递归是美丽的,lambdas是 最终的抽象。但是怎么可能 他们一起使用?兰巴达是 匿名函数和递归 需要名字。
答案 4 :(得分: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);
}
}
答案 5 :(得分:1)
如果您已经达到了递归匿名方法的目的,您可能希望将其提升为您班级中的普通私有方法。