LINQ Expressions are more permissive than C#

When we use Collection initializers, we can’t write many instructions in the initialization:

public class C
{
    public int MyProperty { get; set; }
}


var test = new List<C>() { new C { MyProperty = { int i = 1; i += 1 ; return i; } } };

This code does not compile.

 

But, using LINQ Expressions, it does!

var v = Expression.Variable(typeof(int));
var test = Expression.Lambda<Func<List<C>>>(
    Expression.ListInit(
        Expression.New(typeof(List<C>)), 
        Expression.ElementInit(
            typeof(List<C>).GetMethod("Add"), 
            Expression.MemberInit(
                Expression.New(typeof(C)), 
                Expression.Bind(typeof(C).GetProperty("MyProperty"), 
                Expression.Block(
                    new ParameterExpression[] { v }, 
                    Expression.Assign(v, Expression.Constant(1)), 
                    Expression.AddAssign(v, Expression.Constant(1)), 
                    v)))))).Compile()();






var addthis_share = {
templates: { twitter: ‘{{title}}: {{url}} via @MatthieuMEZIL’ }
}
This entry was posted in 7671, 7672. Bookmark the permalink.

5 Responses to LINQ Expressions are more permissive than C#

  1. I’ll grant you that your example works, but I don’t agree that the two code samples you compare actually do the same thing. It looks like you are assuming that this line:

    var test = new List<C>() { new C { MyProperty = { int  i = 1; i += 1 ; return i; } } };

    initializes MyProperty with the result of a code block. However code blocks in C# don’t allow return statements. They are meant for scope declaration. So you are really assuming that the compiler generates a Func<int> from your code block, which is another thing, and will work.

    Looking at your LINQ expression, where you are using a ParameterExpression, then if you examine the code it actually generates, you will see that it generates, what looks like a Func<int,int> with your parameter being injected into the Func. As mentioned above this is trivial and works (as you show).

    However I believe that the following is a more accurate LINQ expression for what your code line does:

    var v = Expression.Variable(typeof(int));

    var blockExpression = Expression.Block(v,

    Expression.Assign(v, Expression.Constant(1)),

    Expression.AddAssign(v, Expression.Constant(1)),

    v);

    var test = Expression.Lambda<Func<List<C>>>(

    Expression.ListInit(

    Expression.New(typeof(List<C>)),

    Expression.ElementInit(

    typeof(List<C>).GetMethod(“Add”),

    Expression.MemberInit(

    Expression.New(typeof(C)),

    Expression.Bind(typeof(C).GetProperty(“MyProperty”),

    blockExpression))))).Compile()();

    If you try to run this, it will throw an InvalidOperationException with a scope exception.

    Am I missing something, or are you comparing two different things?

  2. Matthieu MEZIL says:

    When you want to use a local variable on a block, you need to use an IEnumerable<ParameterExpression>

    Your code throw an exception but this one works:

     

    var v = Expression.Variable(typeof(int));

    var blockExpression = Expression.Block(

    new ParameterExpression[] { v },

    Expression.Assign(v, Expression.Constant(1)),

    Expression.AddAssign(v, Expression.Constant(1)),

    v);

    var test = Expression.Lambda<Func<List<C>>>(

    Expression.ListInit(

    Expression.New(typeof(List<C>)),

    Expression.ElementInit(

    typeof(List<C>).GetMethod(“Add”),

    Expression.MemberInit(

    Expression.New(typeof(C)),

    Expression.Bind(typeof(C).GetProperty(“MyProperty”),

    blockExpression))))).Compile()();

     

    “Looking at your LINQ expression, where you are using a ParameterExpression, then if you examine the code it actually generates, you will see that it generates, what looks like a Func<int,int> with your parameter being injected into the Func”

    Very interesting. I didn’t try to understand how does it work yet.

    But at the end it’s transparent for me and it’s awesome for one of my real world scenario :)

  3. I know that code works, it’s the code you originally posted :-)

    I wrote my comment mainly to point out that you were comparing two different things:

    1) A code block which defines scope

    2) An implicit Func<int,int>

    So I don’t think it is correct to say that expression trees are more permissive, since you could easily assign the MyProperty using (BTW not sure why a list has to be introduced):

    var list = new List<C>() { new C { MyProperty = new Func<int>(() => { var i = 1; i += 1; return i; })() } };

    The real problem here is that it doesn’t look like a code block. But then again the Expression.Block doesn’t generate a traditional code block, but something more like a Func.

    It would have been nice if the assignment could be made as:

    var list = new List<C>() { new C { MyProperty = () => { var i = 1; i += 1; return i; }() } };

    But since the compiler cannot infer the lambda type it fails. I suspect it is because the expression tree uses lazy evaluation that it can perform the implicit conversion, but I couldn’t say for sure.

  4. Matthieu MEZIL says:

    “It would have been nice if the assignment could be made as:

    var list = new List() { new C { MyProperty = () => { var i = 1; i += 1; return i; }() } };

    But since the compiler cannot infer the lambda type it fails.”

    So LINQ Expressions are more permissive than C# :p

  5. Andrej says:

    Hi folks, I’ve found this googling for my still unanswered question I’ve placed at stackoverflow which relates to this stuff: would you please try to look at it and may be answer it there?

    http://stackoverflow.com/questions/10479417/how-to-bind-parameters-in-replaced-expression-nodes-in-entity-framework-on-the-f

    Thank you guys, Andrej

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>