I'm trying to create an expression lambda to pass an object, and then get the value for named property return. However the type is only known at runtime.
I started with the following method to handle types known at compile time:
private static Func<T, object> CreateExpression(string propertyName)
{
var arg = Expression.Parameter(typeof(T));
var expr = Expression.Property(arg, propertyName);
return Expression.Lambda<Func<T, object>>(expr, arg).Compile();
}
Which worked perfect. However, i need to change it to handle types only known at runtime.
I should be able to call the delegate like this:
public object GetPropertyValue(object obj)
{
var propertyDelegate = GetDelegate(typeof(obj));
var propertyValue = propertyDelegate (obj);
return propertyValue;
}
private Func<object, object> GetDelegate(Type type)
{
// Lookup delegate in dictionary, or create if not existing
return CreateDelegate("MyProperty", type);
}
I tried changing the CreateDelegate from before, but it will not work with Func<object, object>
:
Func<object,object> CreateDelegate(string propertyName, Type targetType)
{
var arg = Expression.Parameter(type);
var body = Expression.Property(arg, name);
var lambda = Expression.Lambda<Func<object,object>>(body, arg); //ArgumentException
return lambda.Compile();
}
It will not accept the Expresion.Parameter, since it is of type 'targetType', and not of type 'object'.
Do i need a Expression.Convert or something?
NOTE: The delegate will be called many times (Filtering method), so it need to be compiled, to ensure performance.
EDIT: Solution (provided by Marc Gravell)
the variable 'body' should be changed to the following:
var body = Expression.Convert(
Expression.Property(
Expression.Convert(arg, type),
name),
typeof(object));
Inner Convert
converts input parameter to object, and the outer Convert
converts the return value.
Yes:
var arg = Expression.Parameter(typeof(object));
var expr = Expression.Property(Expression.Convert(arg, type), propertyName);
Note: the return type (object
) means that many types will need to be boxed. Since you mention you are doing this for filtering: if possible, try to avoid this box by creating instead a Func<object,bool>
that does any comparisons etc internally without boxing.