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.
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 runtimeType
variable isn't.There are several problems with your approach:
fetcherExp.Body
is a member access, e.g.obj.TheProperty
. However, if the expression is of typeExpression<Func<T,object>>
then any value-type property will be represented as a "Convert(obj.TheProperty)".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 typedExpression<Func<T,TProperty>>
- or even just aPropertyInfo
and generate typed getters and setters. Once you have aFunc<T,TProperty> getter
and anAction<T,TProperty> setter
, you can easily wrap those to generate less specific actions and funcs: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 totypedSetter
, 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 expressionExpression<Func<T,TProperty>>
to easily generate untyped getters and setters, and pass those around as your intermediate representation.Please find the example below:
Usage: