为什么匿名方法中不允许使用out参数?

时间:2009-10-28 15:03:56

标签: c# anonymous-methods out-parameters ref-parameters

这不是Calling a method with ref or out parameters from an anonymous method

的欺骗

我想知道匿名方法中不允许使用为什么 out参数。不允许 ref 参数对我来说更有意义,但 out 参数不是那么多。

您对此有何看法

4 个答案:

答案 0 :(得分:29)

在某些方面,这是一个骗局。 Out个参数是ref个参数。 C#语言使用的值只有一个额外的属性。禁止它们的原因与ref参数完全相同。

这里的问题源于在匿名方法中使用匿名方法之外声明的值的效果。这样做会捕获lambda中的值,并且必然会将其生命周期延长到超出当前函数的寿命。这与具有固定生命周期的out参数不兼容。

想象一下,例如out参数引用堆栈上的局部变量。 lambda可以在将来的任意点执行,因此可以在该堆栈帧不再有效时执行。那么out参数意味着什么?

答案 1 :(得分:6)

这基本上与匿名委托/ lambda表达式的参数是捕获变量这一事实有关,并且捕获ref / out变量不会产生任何变量在C#/ CLR中有意义,因为它内部需要ref / out 字段。另外,请注意我将这两个关键字配对,因为它们实际上是相同的。

如果您想在他的博客上提供完整的解释Eric Lippert discussed this design point in detail。 (特别参见底部附近的段落。)

答案 2 :(得分:1)

outref参数之间的唯一区别是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.
}

答案 3 :(得分:1)

我在开发一些错误处理代码时遇到了这个难题。我想将引用(out)传递给将被记录的错误消息。这使我的匿名方法有机会执行多次检查,每次检查都根据需要设置错误消息。

我最终为匿名方法编写了一个新的包装器,它的工作方式不同。但我认为对某人有一些价值的是,我可以简单地创建一个具有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);
    }