如何指定表达式树的返回类型

本文关键字:返回类型 表达式 何指定 | 更新日期: 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();
    }
}