将 return 类型的表达式 <Func<,>> 更改为值类型

Change return type of Expression<Func<,>> with value types

我定义了一个 Expression<Func<TSource, T>>,其 return 类型 (T) 是一个 enum。我正在使用这个和其他类似的表达式来投影和过滤带有 entity framework.

的项目

我还希望能够在只需要数值的上下文中使用此表达式,在这种情况下,将表达式传递/转换为 Expression<Func<TSource, T>>,其中 T 的类型为 int?.

我试过的

This post 展示了如何使用访问者模式在 object 和正常工作的引用类型之间更改 return 类型。但是,当对值类型尝试同样的事情时,它不起作用。

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);
    }
}

[Test]
public void CanChangeFuncReturnTypeFromEnumToInt()
{
    Expression<Func<MyEntity, MyEnum?>> enumExpression = p => p.EnumValue;
    Expression<Func<MyEntity, int?>> intExpression = (Expression<Func<MyEntity, int?>>)new ReturnTypeVisitor<MyEntity, int?>().Visit(enumExpression);
    
    var value = intExpression.Compile().Invoke(new MyEntity { EnumValue = MyEnum.One });

    value.Should().Be(1);
}

public enum MyEnum
{
    One = 1,
    Two = 2
}

public class MyEntity
{
    public MyEnum? EnumValue { get; set; }
}

异常

System.ArgumentException : Expression of type 'System.Nullable`1[Xxx.Filtering.ReturnTypeVisitorVerifications+MyEnum]' cannot be used for return type 'System.Nullable`1[System.Int32]'
   at System.Linq.Expressions.Expression.ValidateLambdaArgs(Type delegateType, Expression& body, ReadOnlyCollection`1 parameters)
   at System.Linq.Expressions.Expression.Lambda(Type delegateType, Expression body, String name, Boolean tailCall, IEnumerable`1 parameters)
   at System.Linq.Expressions.Expression.Lambda(Type delegateType, Expression body, IEnumerable`1 parameters)
   at Filtering.ReturnTypeVisitor`2.VisitLambda[T](Expression`1 node) in xxx\ReturnTypeVisitor.cs:line 11
   at Xxx.Filtering.ReturnTypeVisitorVerifications.CanChangeFuncReturnTypeFromEnumToInt() in yyy\ReturnTypeVisitorVerifications.cs:line 26

问题

如何更改 ReturnTypeVisitor 以使用值类型,或者是否有其他转换表达式的方法?

您可以创建一个扩展方法来转换原始 lambda 的结果:

public static class ExpressionExt {
    public static Expression<Func<TSource,int?>> ConvertToNullableInt<TSource, TEnum>(this Expression<Func<TSource,TEnum>> src)
        => Expression.Lambda<Func<TSource,int?>>(Expression.Convert(src.Body, typeof(int?)), src.Parameters);
}

然后您可以像这样使用它:

Expression<Func<Test,TestEnum>> f = t => t.e;
var g = f.ConvertToNullableInt();