将表达式<Func<Tin, object>> 转换为表达式<Func<Tin, Tout>>

Cast Expression<Func<Tin, object>> to Expression<Func<Tin, Tout>>

我有一个 Expression<Func<Tin, object>> 对象,我需要将它转换为 Expression<Func<Tin, Tout>> 对象。

事实上我有这个:

x => new <>f__AnonymousType6`1(MyProp = x.MyProp)

我需要将其设置为:

x => new MyType(){MyProp = x.MyProp}

请注意,我这里有一个 AnonymousType

为了实现这个我写了一个函数如下:

public static Expression<Func<Tin, Tout>> Transform<Tin, Tout>(this Expression<Func<Tin, object>> source)
{
    var param = Expression.Parameter(typeof(Tout));

    var body = new Visitor<Tout>(param).Visit(source.Body);

    Expression<Func<Tin, Tout>> lambda = Expression.Lambda<Func<Tin, Tout>>(body, param);
    return lambda;
}

还有一个访客class:

class Visitor<T> : ExpressionVisitor
{
    ParameterExpression _parameter;

    public Visitor(ParameterExpression parameter)=>_parameter = parameter;

    protected override Expression VisitParameter(ParameterExpression node)=>_parameter;

    protected override Expression VisitMember(MemberExpression node)
    {
        if (node.Member.MemberType != System.Reflection.MemberTypes.Property)
            throw new NotImplementedException();

        var memberName = node.Member.Name;

        var otherMember = typeof(T).GetProperty(memberName);

        var inner = Visit(node.Expression);
        return Expression.Property(inner, otherMember);
    }
}

但是当我 运行 它时,我得到这个错误:

System.ArgumentException: 'Expression of type '<>f__AnonymousType6`1[System.String]' cannot be used for return type 'MyType''

更新

TinTout classes 中,我有一些参数构造函数和一个私有的无参数构造函数。我 不想 使用参数构造函数,因为它们的参数可能与表达式所需的参数不同。我需要使用私有无参数构造函数构建表达式。

所以如果我使用下面的代码:

var ctor = typeof(TOut).GetPrivateConstructor();
if (ctor != null) // can replace
     return Expression.New(ctor, node.Arguments);

甚至这样:

var ctor = typeof(TOut).GetPrivateConstructor();
    if (ctor != null) // can replace
    {
         var expr = Expression.New(ctor);
         expr.Update(node.Arguments);//<=====Exception in this line
         return expr;
    }

我会得到以下错误:

Incorrect number of arguments for constructor

如果我使用以下内容:

var ctor = typeof(TOut).GetPrivateConstructor();
if (ctor != null) // can replace
     return Expression.New(ctor);

我会错过争论的!

更新 2

如果我将它用作:

var ctor = typeof(TOut).GetPrivateConstructor();
if (ctor != null) // can replace
{
   var expr = Expression.New(ctor);

   FieldInfo argementsField = expr.GetType().GetRuntimeFields().FirstOrDefault(a => a.Name == "_arguments");
   argementsField.SetValue(expr, node.Arguments);

   expr.Update(node.Arguments);
   return expr;
}

表达式将被构建,但不会被执行,因为它会产生以下内容:

x => new MyType(MyProp = x.MyProp)

再次不正确将按预期产生以下错误:

Incorrect number of arguments for constructor

假设 MyType 看起来像这样

public class MyType
{
    public MyType(string myProp)
    {
        MyProp = myProp;
    }

    public string MyProp { get; set; }
}

您可以创建一个通用访客:

public class MyVisitor<TIn, TOut> : ExpressionVisitor
{
    private readonly Type funcToReplace;

    public MyVisitor()
    {
        funcToReplace = typeof(Func<,>).MakeGenericType(typeof(TIn), typeof(object));
    }

    // this hack taken from 
    // and 
    private static bool IsAnonymousType(Type type)
    {
        var markedWithAttribute = type.GetCustomAttributes(
          typeof(CompilerGeneratedAttribute)).Any();
        var typeName = type.Name;

        return markedWithAttribute
          && typeName.StartsWith("<>")
          && typeName.Contains("AnonymousType");
    }

    protected override Expression VisitNew(NewExpression node)
    {
        if (IsAnonymousType(node.Type))
        {
            var arguments = node.Arguments.Select(a => a.Type).ToArray();
            var ctor = typeof(TOut).GetConstructor(arguments);
            if (ctor != null) // can replace
                return Expression.New(ctor, node.Arguments);
        }
        return base.VisitNew(node);
    }

    protected override Expression VisitLambda<T>(Expression<T> node)
    {
        if (typeof(T) != funcToReplace)
            return base.VisitLambda(node);

        var p = node.Parameters.First();
        var body = Visit(node.Body);

        return Expression.Lambda<Func<TIn, TOut>>(body, p);
    }
}

用法:

Expression<Func<TypeOfX, object>> input = x => new {MyProp = x.MyProp};

var visitor = new MyVisitor<TypeOfX, MyType>();
var result = (Expression<Func<TypeOfX, MyType>>) visitor.Visit(input);

一些解释:

VisitNew中我们检查构造函数是否属于匿名类型。如果是这样,我们尝试在 TOut 类型中搜索具有相同参数的构造函数。成功后,我们将匿名类型构造函数替换为来自 TOut

的构造函数