如何指定表达式树的返回类型
本文关键字:返回类型 表达式 何指定 | 更新日期: 2023-09-27 17:51:21
如果我手动构建一个表达式树,运行时似乎会自动确定返回类型。因此,如果我构建一个表达式树,它看起来像这样:
// Order contains a navigation property for Customer
(Order o) => o.Customer;
运行时将返回类型确定为Customer,本质上是这样的:
Expression<Func<Order, Customer>> efd = (Order o) => o.Customer;
我如何构建它?或者更改我构建的内容,所以返回的是对象,即表达式为:
Expression<Func<Order, object>> ef = (Order o) => o.Customer;
这是通用的,在编译时我不知道返回类型是Customer;它可以是(在这种情况下(订单中的任何导航属性。
澄清
假设我从这样一个表达式开始:
Expression<Func<OrderDTO, object>> ef = (OrderDTO o) => o.Customer;
我有一个重建它的例程,通过遍历树将OrderDTO
的类型更改为Order
,并根据从/到类型的映射更改类型。这就是我所要做的,但最终的表达式是
Expression<Func<Order, Customer>> ef = (Order o) => o.Customer;
因此,当我重建树时,我需要以某种方式指定返回类型——看起来系统正在自动确定返回类型,因为我无论如何都没有指定它。谢谢,Ray
很难说没有实际看到您的代码,但看起来您使用的是不允许指定创建的表达式类型的Expression.Lambda()
版本。如果您这样做,那么您的委托类型将自动决定,这是正确的。
要解决此问题,您需要使用Expression.Lambda()
的版本,该版本允许您使用类型参数(例如Expression.Lambda<Func<Order, object>>(…)
(指定委托类型,或者,在您的情况下,更可能的是,将委托类型指定为Type
(Expression.Lambda(funcType, …)
(类型的正常参数的版本。
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);
https://stackoverflow.com/a/71829408/2936295
只需在表达式体上使用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();
}
}