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

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

CS1628:不能在匿名方法、lambda表达式或查询表达式中使用in 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();
}

当前回答

在底层,匿名方法是通过提升捕获的变量(这就是问题主体的全部内容)并将它们存储为编译器生成的类的字段来实现的。没有办法将ref或out形参存储为字段。埃里克·利珀特在一篇博客文章中对此进行了讨论。注意,捕获的变量和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);
}

其他回答

在底层,匿名方法是通过提升捕获的变量(这就是问题主体的全部内容)并将它们存储为编译器生成的类的字段来实现的。没有办法将ref或out形参存储为字段。埃里克·利珀特在一篇博客文章中对此进行了讨论。注意,捕获的变量和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);
}

也许这个呢?

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

不能在lambda表达式中直接使用out形参。你不能那样做的原因在其他答案中有解释。

解决方案

But you can use a local temporary variable with for the inner function and, after the inner function has been executed, assign the out value from the inner function to the out value of the outer function:
private static int OuterFunc (int i_param1, out int o_param2)
{
  int param2 = 0;
  var del    = () => InnerFunc (i_param1, out param2);
  int result = del ();

  o_param2 = param2;
  return result;
}

private static int InnerFunc (int i_param1, out int o_param2)
{
  o_param2 = i_param1;
  return i_param1;
}

private static void Main (string[] args)
{
  int result = OuterFunc (123, out int param2);
  Console.WriteLine (result);  // prints '123'
  Console.WriteLine (param2);  // prints '123'
}

请注意 这个问题创建于2009年。我的答案是在2023年用c# 10和。net 6创建的。我不知道这个答案在2009年是否也适用,这意味着,这里的代码可能依赖于在此期间对c#和。net的增强。

我再举一个例子。

描述

下面的代码将抛出这个错误。因为lambda表达式(i)=>{…}只在函数test中生效。

static void test(out System.Drawing.Image[] bitmaps)
{
    int count = 10;

    bitmaps = new System.Drawing.Image[count];
    Parallel.For(0, count, (i) =>
    {
        bitmaps[i] = System.Drawing.Image.FromFile("2.bmp");
    });
}

解决方案

如果去掉参数,就可以了。

static void test(System.Drawing.Image[] bitmaps)
{
    int count = 10;

    bitmaps = new System.Drawing.Image[count];
    Parallel.For(0, count, (i) =>
    {
        bitmaps[i] = System.Drawing.Image.FromFile("2.bmp");
    });
}

如果真的需要,不要直接更改lambda表达式中的参数。相反,请使用临时变量。

static void test(out System.Drawing.Image[] bitmaps)
{
    int count = 10;

    System.Drawing.Image[] bitmapsTemp = new System.Drawing.Image[count];
    Parallel.For(0, count, (i) =>
    {
        bitmapsTemp[i] = System.Drawing.Image.FromFile("2.bmp");
    });
    bitmaps = bitmapsTemp;
}

可以,但必须显式地定义所有类型

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

无效,但是

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

是有效的