假设我有一个Expression<Func<T,object>>
是否可以根据Type
变量动态更改返回类型Expression<Func<T,int>>
我有以下课程:
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; }
}
我有一个List<ImportCheck<Contact>>
我循环通过,并为Contact
对象设置了一个属性(属性都是不同的类型)。为了使我能够设置嵌套对象的属性,我需要将结果类型与目标类型相同。
如果联系人的所有属性都是int
,那么我现在所拥有的就可以正常运行,因为我有一个不同类型的列表,这让我头疼。
这就是我设置子属性的方法:
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();
}
然后调用MakeSet(member)(target,value)
,其中成员Expression<Func<T,object>>
target
是对象,value
是设置属性的值。
答案 0 :(得分:3)
请找到以下示例:
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);
}
}
用法:
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);
答案 1 :(得分:1)
不确定;你可以创建一个新的表达式树,其中包含从对象到你想要的任何类型的强制转换(当然,没有任何保证人可以保存) - 并且你可以在新表达式中使用旧表达式树的一部分(即整个lambda体)树。
但是,即使你创建了这样的东西,请注意,如果你想静态地表达表达式的类型 - 例如Expression<Func<T,int>>
你需要静态地知道这个类型。泛型可以工作 - 但运行时Type
变量不是。
您的方法存在一些问题:
fetcherExp.Body
是会员访问权限,例如obj.TheProperty
。但是,如果表达式的类型为Expression<Func<T,object>>
,则任何value-type属性都将表示为“Convert(obj.TheProperty)”。Type
属性是正确的。我建议你以不同的方式解决这个问题。我建议您从准确键入的Expression<Func<T,object>>
开始 - 或者甚至只是Expression<Func<T,TProperty>>
并生成,而不是处理不正确键入的PropertyInfo
对象并尝试生成setter(和getter?) 键入 getter和setter。获得Func<T,TProperty> getter
和Action<T,TProperty> setter
之后,您可以轻松地将其包装起来以生成不太具体的操作和功能:
public static Action<T,object> UntypeSetter<T,TProperty>(Action<T,TProperty> typedSetter) =>
(o, val) => typedSetter(o, (TProperty)val);
类似的方法对于getter很有用(但这只对值类型属性的getter很重要,因为covariance意味着引用类型的getter都可以转换为Action<T,object>
)。
如果您绝对需要最大的运行时性能,您可以使用Expression<...>
执行完全相同的包装技巧并内联嵌套调用typedSetter
,但请注意,您没有赢得那么多;对于大多数应用程序来说,一个和两个委托调用之间的区别不大。
TL; DR:不要使用Expression<Func<T,object>>
作为无类型属性的中间表示;这样做会丢弃对创建getter / setter有用的类型信息。相反,使用类型化表达式Expression<Func<T,TProperty>>
可以轻松生成无类型的getter和setter,并将那些作为中间表示传递。