[英]Change return type of Expression<Func<,>> with value types
[英]Changing the return type of an expression<func<>>
假设我有一个Expression<Func<T,object>>
是否可以根据Type
变量动态更改返回类型,使其类似于Expression<Func<T,int>>
我有以下 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; }
}
我有一个List<ImportCheck<Contact>>
循环遍历,并为每个人在Contact
object 上设置一个属性(属性都是不同的类型)。 为了使我能够设置嵌套对象的属性,我需要结果类型与目标类型相同。 如果联系人的所有属性都是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)
,其中 member 是Expression<Func<T,object>>
target
是 object, value
是要将属性设置为的值。
请在下面找到示例:
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);
当然; 您可以创建一个新的表达式树,将对象从对象强制转换为您想要的任何类型(当然,不保证强制转换将保持不变)-并且您可以在新表达式中使用旧表达式树的一部分(即整个 lambda 主体)树。
但是,即使您创建了这样的东西,请注意,如果您想静态地Expression<Func<T,int>>
的类型 - 例如Expression<Func<T,int>>
您将需要静态地知道类型。 泛型会工作 - 但运行时Type
变量不是。
你的方法有几个问题:
fetcherExp.Body
是成员访问,例如obj.TheProperty
。 但是,如果表达式的类型为Expression<Func<T,object>>
则任何值类型属性都将表示为“Convert(obj.TheProperty)”。Type
属性是正确的。 我建议你以不同的方式处理这个问题。 与其处理类型不正确的Expression<Func<T,object>>
对象并尝试从中生成 setter(和 getter?),我建议您从准确类型的Expression<Func<T,TProperty>>
- 甚至只是一个PropertyInfo
并生成类型化的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 有用,因为协方差意味着引用类型 getter 都可以转换为Action<T,object>
)。
如果您绝对需要最大的运行时性能,您可以使用Expression<...>
执行完全相同的包装技巧并将嵌套调用内联到typedSetter
,但请注意,您并没有赢得那么多; 对于大多数应用程序来说,一个和两个委托调用之间的区别不太可能重要。
TL;DR:不要使用Expression<Func<T,object>>
作为无类型属性的中间表示; 这样做会丢弃对创建 getter/setter 有用的类型信息。 相反,使用类型化表达式Expression<Func<T,TProperty>>
轻松生成非类型化 getter 和 setter,并将它们作为中间表示传递。
对于一个非常简单的情况,这是更改 Expression<Func<>> 的返回值的简单方法:
Expression<Func<int, bool>> x = t => t > 1;
var prmi = Expression.Parameter(typeof(int), "i");
var lambda = Expression.Lambda<Func<int, int>>(Expression.Convert(Expression.Invoke(x,prmi), typeof(int)),prmi);
var i = lambda.Compile().Invoke(2); //returns an int
只需在表达式主体上使用Expression.Convert
:
public static class ExpressionHelper
{
public static Expression<Func<TSource, TConvertedResult>> ConvertResult<TSource, TResult, TConvertedResult>(Expression<Func<TSource, TResult>> expression)
{
return Expression.Lambda<Func<TSource, TConvertedResult>>(Expression.Convert(expression.Body, typeof(TConvertedResult)), expression.Parameters);
}
}
测试:
[TestFixture]
public class ExpressionHelperTest
{
public class BaseClass
{
public BaseClass(bool boolean)
{
Boolean = boolean;
}
public bool Boolean { get; set; }
}
public class DerivedClass : BaseClass
{
public DerivedClass(bool boolean, int integer) : base(boolean)
{
Integer = integer;
}
public int Integer { get; set; }
}
[Test]
public void ConvertResult_Simple_Test()
{
Expression<Func<int, bool>> notNullExpression = i => i != 0;
Expression<Func<int, object>> notNullObjectResultExpression = ExpressionHelper.ConvertResult<int, bool, object>(notNullExpression);
Func<int, bool> notNull = notNullExpression.Compile();
Func<int, object> notNullObjectResult = notNullObjectResultExpression.Compile();
Assert.True(notNull(1));
Assert.False(notNull(0));
Assert.AreEqual(true, notNullObjectResult(1));
Assert.AreEqual(false, notNullObjectResult(0));
Assert.Pass();
}
[Test]
public void ConvertResult_Inheritance_Test()
{
Expression<Func<(bool boolean, int integer), DerivedClass>> derivedClassExpression = x => new DerivedClass(x.boolean, x.integer);
Expression<Func<(bool boolean, int integer), BaseClass>> baseClassExpression = ExpressionHelper.ConvertResult<(bool boolean, int integer), DerivedClass, BaseClass>(derivedClassExpression);
Expression<Func<(bool boolean, int integer), DerivedClass>> derivedClassFromBaseClassExpression = ExpressionHelper.ConvertResult<(bool boolean, int integer), BaseClass, DerivedClass>(baseClassExpression);
Func<(bool boolean, int integer), DerivedClass> derivedClass = derivedClassExpression.Compile();
Func<(bool boolean, int integer), BaseClass> baseClass = baseClassExpression.Compile();
Func<(bool boolean, int integer), DerivedClass> derivedClassFromBaseClass = derivedClassFromBaseClassExpression.Compile();
(bool boolean, int integer) trueAndOne = (true, 1);
(bool boolean, int integer) falseAndZero = (false, 0);
Assert.True(derivedClass(trueAndOne).Boolean);
Assert.False(derivedClass(falseAndZero).Boolean);
Assert.AreEqual(1, derivedClass(trueAndOne).Integer);
Assert.AreEqual(0, derivedClass(falseAndZero).Integer);
Assert.True(baseClass(trueAndOne).Boolean);
Assert.False(baseClass(falseAndZero).Boolean);
Assert.True(derivedClassFromBaseClass(trueAndOne).Boolean);
Assert.False(derivedClassFromBaseClass(falseAndZero).Boolean);
Assert.AreEqual(1, derivedClassFromBaseClass(trueAndOne).Integer);
Assert.AreEqual(0, derivedClassFromBaseClass(falseAndZero).Integer);
Assert.Pass();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.