Ho un'espressione ad albero che assomiglia a questo:
.Block(
System.Object $instance,
MyType2 $result) {
$result = (MyType2)((MyType1)$instance).Property1;
.Goto return { };
.Label
.LabelTarget useDefault:;
$result = .Default(MyType2);
.Label
.LabelTarget return:;
$result
}
Questi sono i tipi personalizzati utilizzati nell'albero delle espressioni:
public class MyType1
{
public MyType2 Property1 { get; set; }
}
public class MyType2
{
}
Infine, questo è il modo in cui costruisco, compilo e invoco l'albero delle espressioni (questo non verrà eseguito esattamente in questo modo perché ho lasciato del codice per semplificare le cose):
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);
Il richiamo tuttavia lancia la seguente eccezione:
Il riferimento non impostato su un'istanza di un oggetto. at lambda_method (Closure, Object)
Penso che questo stia accadendo a causa $result = (MyType2)((MyType1)$instance).Property1;
assegnazione ma non capisco perché perché l'istanza che viene passata all'espressione non è null
.
Il fatto che:
ParameterExpression parameter = Expression.Variable(typeof(object));
È definito dopo tutto il corpo dovrebbe essere la chiave; in sostanza, semplicemente non stai nemmeno guardando l'oggetto che passi; stai solo guardando instanceParameter
, che è (nel tuo codice) semplicemente una variabile non assegnata.
In pratica, rilascia la dichiarazione del parameter
finale e non dichiarare instanceParameter
come variabile:
Expression finalExpression = Expression.Block(
new[] { resultVariable },
targetFragments);
MyType2 result = Expression.Lambda<Func<object, MyType2>>(
finalExpression, instanceParameter).Compile()(instance);