Say I have an Expression<Func<T,object>>
is it possible to dynamically change the return type based on a Type
variable to be something like Expression<Func<T,int>>
I have the following class:
public class ImportCheck<T> {
public int id { get; set; }
public string Name { get; set; }
public Type Type { get; set; }
public bool Required { get; set; }
public int? MinLength { get; set; }
public int? MaxLength { get; set; }
public string Value { get; set; }
public Expression<Func<T, object>> AssociatedProperty { get; set; }
}
I have a List<ImportCheck<Contact>>
which I loop through and for each one set a property on the Contact
object (the properties are all different types). To enable me to set the property of nested objects I need the result type to be the same as the target type.
If all the properties of the contact were say int
then what I have now would work fine it's the fact that I have a list of different types that is causing me the headache.
This is how I set a sub property:
private static Action<M, R> MakeSet<M, R>(Expression<Func<M, R>> fetcherExp) {
if (fetcherExp.Body.NodeType != ExpressionType.MemberAccess) {
throw new ArgumentException(
"This should be a member getter",
"fetcherExp");
}
// Input model
var model = fetcherExp.Parameters[0];
// Input value to set
var value = Expression.Variable(typeof(R), "v");
// Member access
var member = fetcherExp.Body;
// We turn the access into an assignation to the input value
var assignation = Expression.Assign(member, value);
// We wrap the action into a lambda expression with parameters
var assignLambda = Expression.Lambda<Action<M, R>>(assignation, model, value);
return assignLambda.Compile();
}
This is then called like MakeSet(member)(target,value)
where member is the Expression<Func<T,object>>
target
is the object and value
is the value to set the property to.
Please find the example below:
public class ReturnTypeVisitor<TSource, TReturnValue> : ExpressionVisitor{
protected override Expression VisitLambda<T>(Expression<T> node)
{
var delegateType = typeof(Func<,>).MakeGenericType(typeof(TSource), typeof(TReturnValue));
return Expression.Lambda(delegateType, Visit(node.Body), node.Parameters);
}
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.DeclaringType == typeof(TSource))
{
return Expression.Property(Visit(node.Expression), node.Member.Name);
}
return base.VisitMember(node);
}
}
Usage:
public class Foo{
public Bar Bar { get; set; }
}
public class Bar { }
Expression<Func<Foo, object>> expression = p => p.Bar;
Expression<Func<Foo, Bar>> stronglyTypedReturnValue =(Expression<Func<Foo, Bar>>) new ReturnTypeVisitor<Foo, Bar>().Visit(expression);
Sure; you can create a new expression tree with a cast from object to whatever type you want (no guarrantees the cast will hold, of course) - and you can use parts of the old expression tree (namely the entire lambda body) in your new expression tree.
However, even if you create such a thing, note that if you want to express the type of the expresion statically - e.g. Expression<Func<T,int>>
you're going to need to know the type statically. Generics would work - but a runtime Type
variable isn't.
There are several problems with your approach:
- You assume that
fetcherExp.Body
is a member access, e.g. obj.TheProperty
. However, if the expression is of type Expression<Func<T,object>>
then any value-type property will be represented as a "Convert(obj.TheProperty)".
- You assume there's a setter corresponding to the getter.
- You assume that the
Type
property is correct.
I suggest you approach this problem differently. Instead of dealing with improperly typed Expression<Func<T,object>>
objects and trying to generate setters (and getters?) from that, I suggest you start from an accurately typed Expression<Func<T,TProperty>>
- or even just a PropertyInfo
and generate typed getters and setters. Once you have a Func<T,TProperty> getter
and an Action<T,TProperty> setter
, you can easily wrap those to generate less specific actions and funcs:
public static Action<T,object> UntypeSetter<T,TProperty>(Action<T,TProperty> typedSetter) =>
(o, val) => typedSetter(o, (TProperty)val);
A similar approach is useful for getters (but this only matters for getters of value-type properties since covariance means that reference type getters can all be cast to Action<T,object>
).
If you absolutely need maximal runtime performance, you can do exactly the same wrapping trick with Expression<...>
s and inline the nested call to typedSetter
, but note that you're not winning that much; the difference between one and two delegate calls is unlikely to matter for most applications.
TL;DR: Don't use Expression<Func<T,object>>
as an intermediate representation of an untyped property; doing so throws away type information useful to creating getters/setters. Instead, use a typed expression Expression<Func<T,TProperty>>
to easily generate untyped getters and setters, and pass those around as your intermediate representation.