将表达式<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''
更新
在 Tin
和 Tout
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
的构造函数
我有一个 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''
更新
在 Tin
和 Tout
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