I have a reflection code, to create an instance of List<>
(type parameter known at run-time), and call Add
method to add some values to it. My snippet is something like this:
// here is my type parameter
var genericType = typeof(MyRunTimeType);
// here is a list of my values
MyRunTimeType[] values = MyRunTimeValuesOfTypeMyRunTimeType();
// creating instance of List<>
var listType = typeof(List<>);
var listGenericType = listType.MakeGenericType(genericType);
var listInstance = Activator.CreateInstance(listGenericType);
// getting Add method and call it
var addMethod = listGenericType.GetMethod("Add", genericType);
foreach (var value invalues)
addMethod.Invoke(listInstance, new[] { value });
So, how would you suggest to convert this reflection snippet to an expression-tree?
UPDATE:
Well, I wrote this snippet, which seems closed to work:
public static Func<IEnumerable<object>, object> GetAndFillListMethod(Type genericType) {
var listType = typeof(List<>);
var listGenericType = listType.MakeGenericType(genericType);
var values = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(genericType), "values");
var ctor = listGenericType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[0], null);
var instance = Expression.Parameter(listGenericType, "list");
var assign = Expression.Assign(instance, Expression.New(ctor));
var addMethod = listGenericType.GetMethod("AddRange", new[] { typeof(IEnumerable<>).MakeGenericType(genericType) });
var addCall = Expression.Call(instance, addMethod, new Expression[] { values });
var block = Expression.Block(
new[] { instance },
assign,
addCall,
Expression.Convert(instance, typeof(object))
);
return (Func<IEnumerable<object>, object>)Expression.Lambda(block, values).Compile();
}
But, I'm getting this error:
Unable to cast object of type
'System.Func`2[System.Collections.Generic.IEnumerable`1[System.String],System.Object]'
to type
'System.Func`2[System.Collections.Generic.IEnumerable`1[System.Object],System.Object]'.
Any suggestion please?
Working:
public static Func<IEnumerable<object>, object> GetAndFillListMethod(Type genericType)
{
var listType = typeof(List<>);
var listGenericType = listType.MakeGenericType(genericType);
var values = Expression.Parameter(typeof(IEnumerable<object>), "values");
var ctor = listGenericType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[0], null);
// I prefer using Expression.Variable to Expression.Parameter
// for internal variables
var instance = Expression.Variable(listGenericType, "list");
var assign = Expression.Assign(instance, Expression.New(ctor));
var addMethod = listGenericType.GetMethod("AddRange", new[] { typeof(IEnumerable<>).MakeGenericType(genericType) });
// Enumerable.Cast<T>
var castMethod = typeof(Enumerable).GetMethod("Cast", new[] { typeof(IEnumerable) }).MakeGenericMethod(genericType);
// For the parameters there is a params Expression[], so no explicit array necessary
var castCall = Expression.Call(castMethod, values);
var addCall = Expression.Call(instance, addMethod, castCall);
var block = Expression.Block(
new[] { instance },
assign,
addCall,
Expression.Convert(instance, typeof(object))
);
return (Func<IEnumerable<object>, object>)Expression.Lambda(block, values).Compile();
}
Your problem is in the fact that you are trying to return a Func<IEnumerable<object>, object>
but your func is in truth a Func<IEnumerable<T>, object>
. The solution is to make the parameter a IEnumerable<object>
and then use Enumerable.Cast<T>
before passing to AddRange
I've changed the Expression.Parameter
used for instance
to a Expression.Variable
... But it is only to make it more clear that it is a variable, not a parameter. The expression tree generated by Expression.Variable
and by Expression.Parameter
is the same (because the two functions have the same code). It is the context where it is used that defines if it is a parameter or a variable. I've done another small change: Expression.Call
doesn't need an explicit array initialization for the parameters.
Ah... And note that the last line of the Block
could be:
addCall,
instance
instead of being
addCall,
Expression.Convert(instance, typeof(object))
because any reference type is implicitly convertible to object
.