views:

76

answers:

1

I have a tree expression that looks like this:

.Block(
    System.Object $instance,
    MyType2 $result) {
    $result = (MyType2)((MyType1)$instance).Property1;
    .Goto return { };
    .Label
    .LabelTarget useDefault:;
    $result = .Default(MyType2);
    .Label
    .LabelTarget return:;
    $result
}

These are the custom types that are used in the expression tree:

public class MyType1
{
    public MyType2 Property1 { get; set; }
}

public class MyType2
{
}

Finally, this is how I build, compile and invoke the expression tree (this won't run exactly like this because I've left out some code to simplify things):

object instance = new MyType1();

Expression expression = ... // n => n.Property1

ParameterExpression instanceParameter = Expression.Variable(
    typeof(object), "instance");
ParameterExpression resultVariable = Expression.Variable(
    typeof(MyType2), "result");

LabelTarget useDefaultLabel = Expression.Label("useDefault");
LabelTarget returnLabel = Expression.Label("return");

List<Expression> targetFragments = new List<Expression>();

MemberInfo memberInfo = (MemberInfo)expression.Body.Member;

MemberExpression member = ConstantExpression.MakeMemberAccess(
    Expression.Convert(instanceParameter, memberInfo.DeclaringType),
    memberInfo);

targetFragments.Add(
    Expression.Assign(
        resultVariable,
        Expression.Convert(member, typeof(MyType2))));

targetFragments.Add(Expression.Goto(returnLabel));
targetFragments.Add(Expression.Label(useDefaultLabel));
targetFragments.Add(Expression.Assign(resultVariable,
    Expression.Default(typeof(MyType2))));
targetFragments.Add(Expression.Label(returnLabel));

targetFragments.Add(resultVariable);

Expression finalExpression = Expression.Block(
    new[] { instanceParameter, resultVariable },
    targetFragments);

ParameterExpression parameter = Expression.Variable(typeof(object));

MyType2 result = Expression.Lambda<Func<T, MyType2>>(expression, parameter)
    .Compile()(instance);

The invoke throws the following exception however:

Object reference not set to an instance of an object. at lambda_method(Closure , Object )

I think this is happening because of the $result = (MyType2)((MyType1)$instance).Property1; assignment but I don't understand why because the instance that is passed to the expression isn't null.

+2  A: 

The fact that:

ParameterExpression parameter = Expression.Variable(typeof(object));

Is defined after all the body should be the clue; essentially, you simply aren't even looking at the object you pass in; you are only looking at instanceParameter, which is (in your code) simply an unassigned variable.

Basically, drop the final parameter declaration, and don't declare instanceParameter as a variable:

Expression finalExpression = Expression.Block(
    new[] { resultVariable },
    targetFragments);

MyType2 result = Expression.Lambda<Func<object, MyType2>>(
      finalExpression, instanceParameter).Compile()(instance);
Marc Gravell
Thanks Marc, that did the trick. I was convinced that the compiler could not tell passed arguments apart (if I had passed additional arguments) so I added the `instanceParameter` to the block. But now I see its obvious it can from the `Func` type parameters.
Sandor Drieënhuizen
Thanks again for helping me out. The question was closely related to a blog post I was writing at http://blog.subspace.nl/post/Getting-rid-of-null-checks-in-property-chains.aspx which may of interest.
Sandor Drieënhuizen