无法在 lambda 表达式中使用 ref 或 out 参数

发布于 2024-08-03 12:23:43 字数 523 浏览 2 评论 0原文

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

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

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

下面是一个简单的示例:

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

Why can't you use a ref or out parameter in a lambda expression?

I came across the error today and found a workaround but I was still curious why this is a compile-time error.

CS1628: Cannot use in ref or out parameter 'parameter' inside an anonymous method, lambda expression, or query expression

Here's a simple example:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

[旋木] 2024-08-10 12:23:43

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

Lambdas have the appearance of changing the lifetime of variables that they capture. For instance, the following lambda expression causes the parameter p1 to live longer than the current method frame as its value can be accessed after the method frame is no longer on the stack

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

Another property of captured variables is that changes to the variables are also visible outside the lambda expression. For example, the following code prints out 42

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

These two properties produce a certain set of effects which fly in the face of a ref parameter in the following ways:

  • ref parameters may have a fixed lifetime. Consider passing a local variable as a ref parameter to a function.
  • Side effects in the lambda would need to be visible on the ref parameter itself. Both within the method and in the caller.

These are somewhat incompatible properties and are one of the reasons they are disallowed in lambda expressions.

↙温凉少女 2024-08-10 12:23:43

在幕后,匿名方法是通过提升捕获的变量(这就是您的问题正文的全部内容)并将它们存储为编译器生成的类的字段来实现的。无法将 refout 参数存储为字段。 Eric Lippert 在 博客条目。请注意,捕获的变量和 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);
}

Under the hood, the anonymous method is implemented by hoisting captured variables (which is what your question body is all about) and storing them as fields of a compiler generated class. There is no way to store a ref or out parameter as a field. Eric Lippert discussed it in a blog entry. Note that there is a difference between captured variables and lambda parameters. You can have "formal parameters" like the following as they are not captured variables:

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);
}
初心未许 2024-08-10 12:23:43

您可以,但必须显式定义所有类型,因此

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

无效,但是

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

有效

You can but you must explicitly define all the types so

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

Is invalid, however

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

Is valid

颜漓半夏 2024-08-10 12:23:43

也许这个?

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

And maybe this?

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();
}
鹤舞 2024-08-10 12:23:43

您不能直接在 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 可能是同时创建的。

You can not use an out parameter directly in a lambda expression. The reason why you can not do that is explained in the other answers.

Workaround

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

Please note
The question was created in 2009. My answer was created in 2023 using C#10 and .NET 6. I don't know whether this answer had also worked back in 2009, which means, the code here might depend on enhancements to C# and .NET that might have been made in the meantime.

情绪操控生活 2024-08-10 12:23:43

我再举一个例子。

描述

下面的代码将抛出此错误。因为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");
    });
}

解决方案

因此,如果您删除参数的out,它就会起作用。

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

如果您确实需要 out,请不要直接更改 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;
}

I will give you another example.

Description

The code below will throw out this error. Because the change brought by the lambda expression (i)=>{...} only works in the function 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");
    });
}

Solution

So, if you remove out of the parameter, it works.

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

If you need out really, don't change the parameter in the lambda expression directly. Instead, use a temporary variable please.

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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文