这不是从匿名方法调用带有 ref 或 out 参数的方法的骗局
我想知道为什么匿名方法中不允许使用 out 参数。不允许ref参数对我来说更有意义,但out参数没有那么多。
你对此有何看法
在某些方面,这是一个骗局。 Out
参数是ref
参数。C# 语言使用的值只是一个额外的属性。不允许它们的原因与参数完全相同ref
。
这里的问题源于在匿名方法中使用在匿名方法之外声明的值的效果。这样做将捕获 lambda 中的值,并且出于必要任意将其生命周期延长到当前函数的生命周期之外。out
这与具有固定生命周期的参数 不兼容。
例如,假设out
参数引用堆栈上的局部变量。lambda 可以在将来的任意点执行,因此可以在该堆栈帧不再有效时执行。那么这个out
参数是什么意思呢?
这基本上与匿名委托/lambda 表达式的参数是捕获变量这一事实有关,并且捕获ref
/out
变量在 C#/CLR 中没有任何意义,因为它在内部需要ref
/out
字段。另外,请注意,我将这两个关键字配对,因为它们实际上是相同的。
如果你想要一个完整的解释,Eric Lippert在他的博客上详细讨论了这个设计点。(具体见底部附近的段落。)
我在开发一些错误处理代码时遇到了这个难题。我想将引用(输出)传递给将被记录的错误消息。这让我的匿名方法有机会执行多个检查,每个检查都根据需要设置错误消息。
我最终为匿名方法编写了一个新的包装器,它的工作方式不同。但我认为可能对某人有价值的是,我可以简单地创建一个具有 out 参数的私有方法,并定义一个委托,并让我的代码使用它。希望这有助于/启发某人。
protected delegate void OutStringDelegate(int divider, out string errorText);
protected void codeWrapper(int divider, OutStringDelegate del)
{
string ErrorMessage = "An Error Occurred.";
try
{
del(divider, out ErrorMessage);
}
catch
{
LogError(ErrorMessage);
}
}
public void UseWrapper(int input)
{
codeWrapper(input, codeToCall);
}
private int somePrivateValue = 0;
private void codeToCall(int divider, out string errorMessage)
{
errorMessage = "Nice Error Message here!";
somePrivateValue = 1 / divider; // call me with zero to cause error.
}
private void LogError(string msg)
{
Console.WriteLine(msg);
}
out
和ref
参数之间的唯一区别是out
参数将[out]
应用一个标记。就 CLR 而言,它们是相同的。
为了实现它,编译器必须生成不支持的ref
字段。
如果你仔细想想,你会意识到允许匿名方法使用out
参数是没有意义的。
下面的代码会做什么?
static Func<object, object> Mess(out object param) {
param = "Original";
return i => param = i;
}
static Func<object, object> MessCaller() {
object local;
return Mess(out local);
}
static vouid Main() {
Console.WriteLine(MessCaller()("New"));
//The local variable that the lambda expression writes to doesn't exist anymore.
}