Coder Perfect

In lambda expressions, you can’t use the ref or out parameters.


In a lambda expression, why can’t you use a ref or out parameter?

I discovered the mistake today and discovered a fix, but I’m still perplexed as to why this is a compile-time error.

Here’s a simple illustration:

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

Asked by skalb

Solution #1

Lambdas provide the impression of altering the lifespan of variables they record. The argument p1 in the following lambda expression, for example, lives longer than the current method frame since its value can be read after the method frame has been removed from the stack.

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

Changes to the variable are likewise visible outside the lambda expression, which is another feature of captured variables. For instance, take a look at print 42.

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

These two qualities provide a series of consequences that are in direct opposition to the ref parameter in the following manner.

These are conflicting qualities, which is why lambda expressions aren’t allowed.

Answered by JaredPar

Solution #2

The anonymous function is implemented behind the scenes by hoisting captured variables (which are what your query body is all about) and saving them as fields of a compiler-generated class. A ref or out parameter cannot be stored as a field. Eric Lippert wrote about it on his blog. There is a distinction to be made between captured variables and lambda parameters. Because they are not captured variables, you can have “formal parameters” like the ones below:

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

Answered by mmx

Solution #3

You can, but you must declare all of the kinds explicitly.

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

Is invalid, however

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

Is valid

Answered by Ben Adams

Solution #4

I feel the need to elaborate on the preceding replies because this is one of the top Google results for “C# lambda ref.” The earlier anonymous delegate syntax (C# 2.0) still works and can handle more complex signatures (as well closures). At the very least, lambdas and anonymous delegates have a similar perceived implementation in the compiler backend (if not identical) – and, more crucially, they both enable closures.

To explain the syntax, here is what I was trying to achieve when I did the search:

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;
            return token;

Just remember that Lambdas are procedurally and mathematically safer (due to the ref value promotion stated earlier): you might potentially unleash a can of worms. When using this syntax, be cautious.

Answered by Jonathan Dickinson

Solution #5

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

Answered by ticky

Post is based on