Cannot use ref or out parameter in lambda expressions
Asked Answered
A

6

217

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();
}
Allow answered 2/9, 2009 at 3:22 Comment(5)
It's about iterators, but much of the same reasoning in this post (also by Eric Lippert &mdash; he is on the language design team after all) applies to lambdas: <blogs.msdn.com/ericlippert/archive/2009/07/13/…>Trangtranquada
May I ask what was the workaround that you had found ?Cushman
You can just declare a local normal variable and work with that, and assign the result to value afterwards... Add a var tempValue = value; and then work with tempValue.Ind
The article @JoelCoehoorn's comment refers to can now be found hereBisset
@Beatles1692: This is the workaround that I found: https://mcmap.net/q/125461/-cannot-use-ref-or-out-parameter-in-lambda-expressionsToothsome
S
144

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.

Spurious answered 2/9, 2009 at 4:43 Comment(1)
if you still want to use it, then you can create a temp variable and use that inside lamda. something like int tempVariable = refVariable; int newValue = array.Where(a => a == tempVariable).First();Semitics
H
92

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);
}
Horaciohorae answered 2/9, 2009 at 3:26 Comment(0)
W
79

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

Wun answered 22/11, 2015 at 4:19 Comment(8)
It does; question is why can't you; answer is you can.Wun
It doesn't; question is why you can not reference an existing variable, already defined ref or out, inside a lambda. It is clear if you read the example code (try again to read it again). The accepted answer clearly explain why. Your answer is about using ref or out parameter to the lambda. Totally not answering the question and speaking about something elseTricorn
@Tricorn is right ... this has nothing to do with the subject of the question, which is about the content of the lamba expression (on the right), not its parameter list (on the left). It's bizarre that this got 26 upvotes.Ebonieebonite
But I still don't understand why it has been designed to be like this. Why do I have to explicitly define all types? Semantically I don't need to. Am I losing something?Ceraceous
@Ceraceous I think that the reason is syntactical.Crowder
@JimBalter this answer is likely being upvoted because this question is #1 for search engines when looking for "c# lambda ref"Mae
@Ceraceous because ref qualifies a type, not a variable; ref d doesn't mean anything, but ref int does. While they could have fudged the syntax to allow ref d for convenience, they chose not to. As to why they don't allow (a, b, c, ref int d) ... who knows? Submit a proposal for a language change.Ebonieebonite
The OP refers to a lambda expression, where the type has already be defined in the function/delegate definition. The question is (a,b) is compilable for an (int,int) function, but (int a, ref int b) is required for (int, ref int)`.Ceraceous
R
3

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();
}
Reassure answered 19/12, 2019 at 11:32 Comment(0)
T
1

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.

Toothsome answered 6/2, 2023 at 13:36 Comment(0)
I
0

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;
}
Ivon answered 15/12, 2022 at 13:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.