将Expression <func <tin,object =“” >>转换为Expression <func <tin,tout =“” >>

时间:2019-04-13 14:46:28

标签: c# expression func visitor

我有一个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 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:'类型的表达   '<> f__AnonymousType6`1 [System.String]'不能用于返回类型   “ MyType”

更新

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

因此,如果我使用以下代码:

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

我将收到以下错误:

  

构造函数的参数数量错误

如果我使用以下内容:

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)

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

  

构造函数的参数数量错误

1 个答案:

答案 0 :(得分:2)

假设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 https://stackoverflow.com/a/2483054/4685428
    // and https://stackoverflow.com/a/1650895/4685428
    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

中的构造函数替换匿名类型构造函数