我有一个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”
更新
在Tin
和Tout
类中,我有一些参数构造函数和一个私有的无参数构造函数。我不希望使用参数构造函数,因为它们的参数可能与所需的表达式不同。我需要使用无私有参数的构造函数来构建表达式。
因此,如果我使用以下代码:
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)
再次不正确的地方将按预期产生以下错误:
构造函数的参数数量错误
答案 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