I am trying to implement a method that builds a Func<,>
dynamically, depending on what types are provided. The Func
itself is supposed to simply call a static method and return its first parameter. I have a few dummy classes:
public class Foo { }
public class Foo1 : Foo { }
public class Foo2 : Foo { }
public class Foo3 : Foo { }
And the usage of method looks like this:
var genericTypes = new List<Type>() { typeof(Foo1), typeof(Foo2), typeof(Foo3) };
var func = BuildFunc(genericTypes);
var res = func.DynamicInvoke(new Foo1(), new Foo2(), new Foo3());
When I was proptotyping it was working fine. However when I tried to implement it properly I encountered a strange behaviour which I can't explain.
Here is how I implemented the method:
private static Delegate BuildFunc(List<Type> genericTypes)
{
List<ParameterExpression> parameters = new List<ParameterExpression>();
for (int i = 0; i < genericTypes.Count; i++)
{
var param = Expression.Parameter(genericTypes[i], "arg" + i);
parameters.Add(param);
}
var methodCallInfo = typeof(Program).GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Single(a => a.Name == "PopulateChildren");
var parameterArray = Expression.NewArrayInit(typeof(Foo), parameters);
var callExpr = Expression.Call(methodCallInfo,
parameterArray);
//return arg0
LabelTarget returnTarget = Expression.Label(type: parameters[0].Type);
GotoExpression returnExpression = Expression.Return(returnTarget,
parameters[0], parameters[0].Type);
var returnLabel = Expression.Label(returnTarget, parameters[0]);
//assembling & compiling
var assembled = Expression.Block(
parameters,
callExpr,
returnExpression,
returnLabel);
var compiled = Expression.Lambda(assembled, parameters).Compile();
return compiled;
}
public static void PopulateChildren(Foo[] objects)
{
}
As you can see the code above defines parameters, initializes an array with parameters, makes a call to a method and returns its first parameter.
Now if you run this code and step throught PopulateChildren
method - you will get objects
collection with 3 null values.
And if you change
var assembled = Expression.Block(
parameters,
callExpr,
returnExpression,
returnLabel);
to
var assembled = Expression.Block(
parameters[0],
parameters[1],
parameters[2],
callExpr,
returnExpression,
returnLabel);
Now I discovered that different signatures of Expression.Block
contain either has no variables
or that contains the given variables
words in its summary.
I found signatures for this method overloads here http://ift.tt/2xFo9EC and I thought to myself: "Ok, I'm just stupid and accidently used a wrong signature. I will use a right one!"
So I chose this one: Block(IEnumerable<ParameterExpression>, IEnumerable<Expression>)
which has a description Creates a BlockExpression that contains the given variables and expressions.
So I changed the abovesaid code to
var expresssion = new List<Expression>()
{
callExpr,
returnExpression,
returnLabel
};
var assembled = Expression.Block(parameters, expresssion);
And it still gives me null values!
Can someone please explain to me what is going on and what I am missing? And most importantly how do I call Expression.Block
in a way that accepts a collection of parameters and doesn't turn them to null?
Aucun commentaire:
Enregistrer un commentaire