【问题标题】:Why is an out parameter not allowed within an anonymous method?为什么匿名方法中不允许使用 out 参数?
【发布时间】:2026-02-17 22:35:01
【问题描述】:

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

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

您对此有何看法

【问题讨论】:

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


【解决方案1】:

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

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

例如,假设out 参数引用了堆栈上的一个局部变量。 lambda 可以在将来的任意点执行,因此可以在该堆栈帧不再有效时执行。那么out 参数是什么意思呢?

【讨论】:

  • +1 已开始输入与您上一段类似的示例。谢谢你省了我的麻烦:)
  • 第二段打开了灯泡。谢谢@JaredPar!
  • 但这不适用于任何其他类型的变量吗?我的意思是,如果我在位于堆栈中的 lambda 表达式中使用变量,只需访问某个字段。如果 lambda 表达式在堆栈不再有效时执行,对象不是已经被收集了吗?如果不是,垃圾收集器何时会收集该对象?
  • @Jorge,如果您在 lambda 表达式中使用局部变量,它将永远不会位于堆栈上。闭包捕获机制实际上会将其完全从方法中删除(使用反射器检查输出)。参数略有不同,它们只是在方法开始时复制到 lambda 中。
  • 一个小问题:ref 参数的“outness”不是由 modopt 表示的,而是由属性表示的。使用 modopt 可能是一个更好的主意,因为这样您就可以拥有两个具有相同签名的方法,它们仅在 out/ref-ness 上有所不同。但是在评估签名相等性时不考虑属性。
【解决方案2】:

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

如果您想要完整的解释,请在他的博客上Eric Lippert discussed this design point in detail。 (具体见底部附近的段落。)

【讨论】:

    【解决方案3】:

    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.
    }
    

    【讨论】:

    • OutAttribute 属性不是应用于 C# 中的 out 参数的东西。 OutAttribute 是编组时考虑的自定义属性。 out 参数由另一个元数据令牌 ([out] in IL) 注释。您可以通过查询在 C# 代码中声明为 outParameterInfo 的属性来验证这一事实。那里没有[OutAttribute]
    【解决方案4】:

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

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

    【讨论】: