locked
Lambda Parameter not in scope

    Question

  • I'm trying to compose the first block of expression using expression trees. The second block compiles fine. But the third block generates a "Lambda Parameter not in scope" error. All 3 blocks generate the same expression "{(x, y) => Multiply(x, y)}".

    There seems to be some pre-condition in Expression.Multiply that checks whether the ParameterExpressions passed to it is in the list of expression passed to the lambda expression. I'm assuming the the call to Expression.Parameter( typeof(int), "x" ),   would resolve to the correct ParameterExpression cause the contents are the same. Shouldn't there be some sort of equality/identity thing enforced on Expressions similar to how string equality works? If not, Expression.ParameterReference( "x" ) would have been a better element in the generated expression tree rather than ParameterExpression as it is more consistent with the intention of the developer.

    {

    Expression<Func<int, int, int>> addExpression = (x, y) => x * y;

    Console.WriteLine( addExpression );

    Func<int, int, int> add = addExpression.Compile();

    Console.WriteLine( add(10, 20) );

    }

    {

    ParameterExpression paramX = Expression.Parameter( typeof(int), "x" );

    ParameterExpression paramY = Expression.Parameter( typeof(int), "y" );

    var addExpression = Expression.Lambda<Func<int, int, int>>(

    Expression.Multiply( paramX, paramY ),

    new List<ParameterExpression> { paramX, paramY }

    );

    Console.WriteLine( addExpression );

    var add = addExpression.Compile();

    Console.WriteLine( add(10, 20) );

    }

    {

    //Whats  wrong with this code???

    var addExpression = Expression.Lambda<Func<int, int, int>>(

    Expression.Multiply(

    Expression.Parameter( typeof(int), "x" ),  //Expression.ParameterReference( "x" ), ???

    Expression.Parameter( typeof(int), "y"//Expression.ParameterReference( "y" )

    ) ,

    new List<ParameterExpression> {

    Expression.Parameter( typeof(int), "x" ),

    Expression.Parameter( typeof(int), "y" )

    }

    );

    Console.WriteLine( addExpression );

    var add = addExpression.Compile();

    Console.WriteLine( add(10, 20) );

    }

    Thursday, March 15, 2007 5:35 AM

Answers

  • Parameters are referenced in expressions through their object identity, not by comparing their names. In fact, from an expression tree's point of view, the name of a parameter is purely informational. The reason for this design is the same reason that types are referenced through their System.Type objects and not their names--expression trees are fully bound and are not in the business of implementing name lookup rules (which may differ from language to language).

    So, in your third example, you end up with four distinct parameter objects, two x's and two y's, but only two of them are accessible in the lambda expression.

    Anders

     

    Thursday, March 15, 2007 2:37 PM

All replies

  • Parameters are referenced in expressions through their object identity, not by comparing their names. In fact, from an expression tree's point of view, the name of a parameter is purely informational. The reason for this design is the same reason that types are referenced through their System.Type objects and not their names--expression trees are fully bound and are not in the business of implementing name lookup rules (which may differ from language to language).

    So, in your third example, you end up with four distinct parameter objects, two x's and two y's, but only two of them are accessible in the lambda expression.

    Anders

     

    Thursday, March 15, 2007 2:37 PM
  • Why is there a second argument in Expression.Lambda<>() in the first place? Can't it be extracted from the first argument? I am aware that it might be hard to do as of the May CTP, but this is really an API usability question.

    Sean

    Friday, March 16, 2007 6:47 AM
  •  Anders Hejlsberg wrote:

    Parameters are referenced in expressions through their object identity, not by comparing their names. In fact, from an expression tree's point of view, the name of a parameter is purely informational. The reason for this design is the same reason that types are referenced through their System.Type objects and not their names--expression trees are fully bound and are not in the business of implementing name lookup rules (which may differ from language to language).

    I agree. It makes perfect sense from an implementation perspective. But from a usability stand point it would be more helpful if there's an Expression.ParameterReference("x") or ParameterReferenceExpression  or something to that effect in the generated expression tree. This makes it obvious to the user that parameters are resolved in the context of the lambda in its compilation scope.

    Friday, March 16, 2007 12:20 PM
  • Regarding the second parameter to Expression.Lambda<>(), it isn't really possible to infer the lambda expression's parameter list from its body. For example, we wouldn't know what order to place the parameters in. Also, a lambda expression that contains a nested lambda expression which refers to parameters of the outer lambda (as in a nested query) would be problematic.

    Anders

    Friday, March 16, 2007 2:52 PM
  • It would be enough to extend the Parameters collection with the indexer by parameter name. It gives the possibility to refer to used parameters.

     

    Thanks

    Monday, October 08, 2007 12:07 AM
  • Hi,

    is there now, 2 years later, a solution for that or a workaround? Actually i get a lambda with one parameter and i need to cast the parameter to another type and than take the entire lambda expression with the new parameter and use this later... Are there ways to do that?

    Nico


    VB.NET & C#.NET
    Saturday, October 10, 2009 3:26 PM
  • is there now, 2 years later, a solution for that or a workaround? Actually i get a lambda with one parameter and i need to cast the parameter to another type and than take the entire lambda expression with the new parameter and use this later... Are there ways to do that?


    Yes, I recently posted a code snippet that will allow you to replace an out-of-scope parameter (or any other parameter) with a new one, cloning the expression tree in the process:
    http://huagati.blogspot.com/2009/10/code-sample-search-and-replace-in-linq.html




    Kristofer - Huagati Systems Co., Ltd. - Cool tools for Linq-to-SQL and Entity Framework: www.huagati.com/dbmltools (VS designer add-in), www.huagati.com/L2SProfiler (query profiler for L2S)
    Sunday, October 11, 2009 11:50 AM