2010-08-01 3 views
1

J'ai une expression d'arbre qui ressemble à ceci:Pourquoi est-ce que j'obtiens une exception de référence nulle dans cet arbre d'expression?

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

Ce sont les types personnalisés qui sont utilisés dans l'arbre d'expression:

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

public class MyType2 
{ 
} 

Enfin, voici comment je construis, compiler et invoquer l'arbre d'expression (cela ne fonctionne pas exactement comme ça parce que j'ai laissé de code pour simplifier les choses):

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

Invoke lance l'ex suivant Ception cependant:

La référence d'objet n'est pas définie sur une instance d'un objet. à lambda_method (fermeture, objet)

Je pense que ce qui se passe en raison de l'affectation $result = (MyType2)((MyType1)$instance).Property1; mais je ne comprends pas pourquoi, parce que l'instance qui est passé à l'expression n'est pas null.

Répondre

3

Le fait que:

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

est défini après tout le corps doit être l'indice; Essentiellement, vous ne regardez même pas l'objet que vous dépassez; vous ne regardez que instanceParameter, qui est (dans votre code) simplement une variable non affectée.

Fondamentalement, déposez la déclaration finale parameter, et non communication instanceParameter comme variable:

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

MyType2 result = Expression.Lambda<Func<object, MyType2>>(
     finalExpression, instanceParameter).Compile()(instance); 
+0

Merci Marc, qui a fait l'affaire. J'étais convaincu que le compilateur ne pouvait pas distinguer les arguments passés (si j'avais passé des arguments supplémentaires) alors j'ai ajouté le 'instanceParameter' au bloc. Mais maintenant je vois que c'est évident à partir des paramètres de type 'Func'. –

+0

Merci encore de m'avoir aidé. La question était étroitement liée à un article de blog que j'écrivais à http://blog.subspace.nl/post/Getting-rid-of-null-checks-in-property-chains.aspx, qui pourrait vous intéresser. –

Questions connexes