Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
365 views
in Technique[技术] by (71.8m points)

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

I have defined an Expression<Func<TSource, T>> whose return type (T) is an enum. I am using this and other similar expressions for projecting and filtering items with entity framework.

I would also like to be able to use this expression within a context where only numeric values are expected and in that case pass / convert the expression as Expression<Func<TSource, T>> where T is of type int?.

What I have tried

This post shows how to use the Visitor pattern to change the return type between object and a reference type which works fine. It does however not work when trying the same thing with value types.

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

Exception

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 xxxReturnTypeVisitor.cs:line 11
   at Xxx.Filtering.ReturnTypeVisitorVerifications.CanChangeFuncReturnTypeFromEnumToInt() in yyyReturnTypeVisitorVerifications.cs:line 26

The question

How can I change the ReturnTypeVisitor to work with value types, or is there another way of converting the expression?

question from:https://stackoverflow.com/questions/65904862/change-return-type-of-expressionfunc-with-value-types

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

You can create an extension method to convert the result of the original 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);
}

which you can then use like:

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

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...