【问题标题】:Cannot use ref or out parameter in lambda expressions不能在 lambda 表达式中使用 ref 或 out 参数
【发布时间】:2010-11-24 20:10:22
【问题描述】:

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

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

CS1628: 不能在匿名方法、lambda 表达式或查询表达式中使用 in ref 或 out 参数“参数”

这是一个简单的例子:

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();
}

【问题讨论】:

  • 这是关于迭代器的,但这篇文章中的大部分推理(同样由 Eric Lippert 撰写,毕竟他是语言设计团队的成员)适用于 lambda:blogs.msdn.com/ericlippert/archive/2009/07/13/…>
  • 请问您找到的解决方法是什么?
  • 您可以声明一个局部普通变量并使用它,然后将结果分配给 value... 添加一个 var tempValue = value;然后使用 tempValue。
  • 文章@JoelCoehoorn的comment指的现在可以找到here

标签: c# lambda


【解决方案1】:

Lambda 看起来会改变它们捕获的变量的生命周期。例如,下面的 lambda 表达式会导致参数 p1 比当前方法框架存活,因为它的值可以在方法框架不再在堆栈上之后访问

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 表达式中不允许使用它们的原因之一。

【讨论】:

  • 如果你还想使用它,那么你可以创建一个临时变量并在 lamda 中使用它。类似int tempVariable = refVariable; int newValue = array.Where(a =&gt; a == tempVariable).First();
【解决方案2】:

在后台,匿名方法是通过提升捕获的变量(这是您的问题主体的全部内容)并将它们存储为编译器生成的类的字段来实现的。无法将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);
}

【讨论】:

    【解决方案3】:

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

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

    但是无效

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

    有效

    【讨论】:

    • 确实如此;问题是你为什么不能;答案是可以的。
    • 它没有;问题是为什么您不能在 lambda 中引用已定义 refout现有变量。如果您阅读示例代码,则很清楚(再次尝试再次阅读)。接受的答案清楚地解释了原因。您的答案是关于使用 refout parameter 到 lambda。完全不回答问题并谈论其他事情
    • @edc65 是对的......这与问题的主题无关,这是关于lamba表达式的内容(在右边),而不是它的参数列表(在左边)。奇怪的是,这得到了 26 票。
    • 但我还是不明白为什么它被设计成这样。为什么我必须明确定义所有类型?语义上我不需要。我失去了什么吗?
    • @joe 我认为原因是语法上的。
    【解决方案4】:

    因为这是 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;
            };
    }
    

    请记住,Lambda 在程序和数学上更安全(因为前面提到的 ref 值提升):您可能会打开一罐蠕虫。使用此语法时请仔细考虑。

    【讨论】:

    • 我认为您误解了这个问题。问题是为什么 lambda 不能在其容器方法中访问 ref/out 变量,而不是为什么 lambda 本身 不能包含 ref/out 变量。 AFAIK 后者没有充分的理由。今天我写了一个 lambda (a, b, c, ref d) =&gt; {...}ref 是红色下划线的错误消息“参数'4'必须用'ref'关键字声明”。捂脸!附言什么是“参考价值提升”?
    • @Qwertie 我得到这个可以完全参数化,意思是,包括 a、b、c 和 d 上的类型,它可以工作。请参阅 BenAdams 的回答(尽管他也误解了最初的问题)。
    • @Qwertie 我想我只删除了该点的一半 - 我认为最初的观点是将 ref 参数放入闭包中可能有风险,但我随后一定意识到这并没有发生在我给出的例子(我也不知道它是否会编译)。
    • 这与实际提出的问题无关......请参阅 Ben Adams 的已接受答案和答案下的 cmets,他同样误解了这个问题。
    【解决方案5】:

    也许这个?

    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 == val).First();
    }
    

    【讨论】:

      猜你喜欢
      • 2017-03-25
      • 1970-01-01
      • 2011-08-29
      • 2021-10-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-13
      • 1970-01-01
      相关资源
      最近更新 更多