不能在lambda表达式中使用ref或out参数

时间:2009-09-02 03:22:44

标签: c# lambda

为什么不能在lambda表达式中使用ref或out参数?

我今天遇到了错误并找到了解决方法,但我仍然很好奇为什么这是编译时错误。

  

CS1628:无法在匿名方法,lambda表达式或查询表达式中的ref或out参数'parameter'中使用

这是一个简单的例子:

private void Foo()
{
    int value;
    Bar(out value);
}

private void Bar(out int value)
{
    value = 3;
    int[] array = { 1, 2, 3, 4, 5 };
    int newValue = array.Where(a => a == value).First();
}

5 个答案:

答案 0 :(得分:111)

Lambdas看起来更改了他们捕获的变量的生命周期。例如,以下lambda表达式导致参数p1 live 比当前方法帧更长,因为在方法帧不再位于堆栈之后可以访问其值

Func<int> Example(int p1) {
  return () => p1;
}

捕获变量的另一个属性是变量的变化在lambda表达式之外也是可见的。例如,以下打印42

void Example2(int p1) {
  Action del = () => { p1 = 42; }
  del();
  Console.WriteLine(p1);
}

这两个属性产生一系列效果,它们以下列方式面对ref参数

  • ref参数可能具有固定的生命周期。考虑将局部变量作为ref参数传递给函数。
  • lambda中的副作用需要在ref参数本身上可见。在方法和调用者中。

这些属性有些不兼容,是lambda表达式中不允许使用它们的原因之一。

答案 1 :(得分:79)

在幕后,匿名方法是通过提升捕获的变量(这是你的问题主体所有)来实现的,并将它们存储为编译器生成的类的字段。无法将refout参数存储为字段。 Eric Lippert在a blog entry中对此进行了讨论。请注意,捕获的变量和lambda参数之间存在差异。你可以拥有如下所示的“形式参数”,因为它们不是捕获的变量:

delegate void TestDelegate (out int x);
static void Main(string[] args)
{
    TestDelegate testDel = (out int x) => { x = 10; };
    int p;
    testDel(out p);
    Console.WriteLine(p);
}

答案 2 :(得分:58)

您可以但必须明确定义所有类型

(a, b, c, ref d) => {...}

无效,但是

(int a, int b, int c, ref int d) => {...}

有效

答案 3 :(得分:5)

因为这是Google上“C#lambda ref”的最佳成绩之一;我觉得我需要扩展上面的答案。旧的(C#2.0)匿名委托语法有效,它确实支持更复杂的签名(以及闭包)。 Lambda和匿名委托至少在编译器后端共享感知实现(如果它们不相同) - 最重要的是,它们支持闭包。

我在搜索时尝试做的事情,以演示语法:

public static ScanOperation<TToken> CreateScanOperation(
    PrattTokenDefinition<TNode, TToken, TParser, TSelf> tokenDefinition)
{
    var oldScanOperation = tokenDefinition.ScanOperation; // Closures still work.
    return delegate(string text, ref int position, ref PositionInformation currentPosition)
        {
            var token = oldScanOperation(text, ref position, ref currentPosition);
            if (token == null)
                return null;
            if (tokenDefinition.LeftDenotation != null)
                token._led = tokenDefinition.LeftDenotation(token);
            if (tokenDefinition.NullDenotation != null)
                token._nud = tokenDefinition.NullDenotation(token);
            token.Identifier = tokenDefinition.Identifier;
            token.LeftBindingPower = tokenDefinition.LeftBindingPower;
            token.OnInitialize();
            return token;
        };
}

请记住,Lambdas在程序上和数学上更安全(因为前面提到的ref值促销):你可能会打开一堆蠕虫。使用这种语法时请仔细考虑。

答案 4 :(得分:0)

也许是这个?

private void Foo()
{
    int value;
    Bar(out value);
}

private void Bar(out int value)
{
    value = 3;
    int[] array = { 1, 2, 3, 4, 5 };
    var val = value; 
    int newValue = array.Where(a => a == var).First();
}