lundi 25 septembre 2017

Expression.Block different signatures change parameters values to null

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