3

I want to validate my assumption that the LINQ Expression API does not have any means for us to create an expression that represents the creation of a local variable.

In other words, you cannot create an expression to represent:

int local;

since that is a variable declaration statement, and the API does not support statement lambdas. The only state that a lambda expression, as represented by the LINQ Expression API (and not a delegate instance) can work with is parameters it receives and the captured variables it receives via a closure.

Is my assumption (based on a few months of practice of the LINQ Expression API) correct?

Water Cooler v2
  • 32,724
  • 54
  • 166
  • 336
  • 1
    Do you mean if .NET expression trees support declarations inside the "expression"? So for example if you try `System.Linq.Expressions.Expression expr = () => { int local; };` (try to build the expression tree from a C# lambda), you get the error _error CS0834: A lambda expression with a statement body cannot be converted to an expression tree_. So it is not supported for C# lambda arrows, at the least. – Jeppe Stig Nielsen May 08 '15 at 11:03

2 Answers2

9

False. There are some overloads of Expression.Block to do it.

What is true is that you can't create a lambda expression through the use of the C# compiler that has a variable, but that is a limitation of the compiler.

So you can't

Expression<Func<int>> exp = () => {
    int v = 1;
    return v;
};

but you can

var variable = Expression.Variable(typeof(int));
var lambda = Expression.Lambda<Func<int>>(
    Expression.Block(
        new[] { variable }, 
        Expression.Assign(variable, Expression.Constant(1)), 
        variable)); // With lambda expressions, there is an implicit
                    // return of the last value "loaded" on the stack

since that is a variable declaration statement, and the API does not support statement lambdas.

This was true in .NET < 4.0 . In .NET 4.0 Microsoft added Expression methods to build nearly everything that can be present in the body of a method (there are some missing "things", like unsafe code keywords/operators, plus there are the primitives but there aren't complex constructs like the for or lock, that can be built on top of other constructs). Note that 90% of those added things are incompatible with LINQ-to-SQL/EF.

xanatos
  • 109,618
  • 12
  • 197
  • 280
5

Well, you can use Expression.Block to declare a block which contains local variables...

For example:

using System;
using System.Linq.Expressions;

public class Test
{    
    static void Main()
    {
        var x = Expression.Variable(typeof(int), "x");
        var assignment1 = Expression.Assign(x, Expression.Constant(1, typeof(int)));
        var assignment2 = Expression.Assign(x, Expression.Constant(2, typeof(int)));

        var block = Expression.Block(new[] { x }, new[] { assignment1, assignment2 });
    }
}

That builds an expression tree equivalent to:

{
    int x;
    x = 1;
    x = 2;
}

The C# compiler doesn't use this functionality within lambda expression conversions to expression trees, which are currently still restricted to expression lambdas, as far as I'm aware.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Thank you very much. I am confused now. I need to investigate some more. The cause of my confusion was the code inside the `RequiresCanWrite` method, which is called from within, for one example, the `Expression.Assign` factory method. It makes sure that the left-hand-side of the assignment is either a parameter, or an indexer (for an array or property) or a member access expression (property or field). That led me to want to test if I could create a local and assign to it. I see inside the code for the `Expression.Variable` factor that it creates a parameter expression. To be contd... – Water Cooler v2 May 08 '15 at 11:15
  • I want to think some more before I type more comments. Another point of confusion is that C# has block scope. So, is that variable you created inside a block really a local variable? Do blocks have their own stack-frames in the CLR? – Water Cooler v2 May 08 '15 at 11:18
  • 1
    @WaterCoolerv2: This is a *representation* of code - it's not IL. And blocks don't typically have separate stack frames within normal C# anyway... – Jon Skeet May 08 '15 at 11:19
  • Sorry, I should have preluded my question with, "if compiled...". I get the fact that these are all representations and not IL. – Water Cooler v2 May 08 '15 at 11:20
  • @WaterCoolerv2: I suggest you think about exactly what you want "really a local variable" to mean and then test it... – Jon Skeet May 08 '15 at 11:21
  • Thanks. Yes, I need to stop and think more. – Water Cooler v2 May 08 '15 at 11:22
  • And, as seen in the other answer, your `block` can be turned into an `Expression` with this method: `var lambda = Expression.Lambda(block);` – Jeppe Stig Nielsen May 08 '15 at 13:47
  • @JeppeStigNielsen I don't think that a block ever *turns into* a lambda. A block simply is a base class for defining a scope. I could be wrong, but that's what I infer. You could have a block that spans over the entire breath of the lambda's body, thus defining a method scope like scope. But even then, that's just a scope. A lambda is a very different beast. – Water Cooler v2 May 08 '15 at 15:00
  • @WaterCoolerv2 I said it was turned into an `Expression` which is certainly correct. The names of the method and of the local variable are just — names. – Jeppe Stig Nielsen May 08 '15 at 17:31
  • Sorry, I understand what you were saying. You're right. I was being irritably pedantic and perhaps even wrong. – Water Cooler v2 May 08 '15 at 18:17