I have an Expression<Func<Tin, object>>
object and I need to cast it to the Expression<Func<Tin, Tout>>
object.
In fact I have this:
x => new <>f__AnonymousType6`1(MyProp = x.MyProp)
and I need to have it as:
x => new MyType(){MyProp = x.MyProp}
Note that I have an AnonymousType
here!
To achieve this I wrote a function as below:
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;
}
And a Visitor 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);
}
}
But when I run it, I get this error:
System.ArgumentException: 'Expression of type '<>f__AnonymousType6`1[System.String]' cannot be used for return type 'MyType''
Update
In Tin
and Tout
classes I have some parametric constructors and a private parameter-less constructor. I do not want to use parametric constructors as they have arguments which might be different from the expression required one. I need to build the Expression using private parameter-less constructor.
So if I use the below code:
var ctor = typeof(TOut).GetPrivateConstructor();
if (ctor != null) // can replace
return Expression.New(ctor, node.Arguments);
Or even this:
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;
}
I will get the following error:
Incorrect number of arguments for constructor
And if I use the following:
var ctor = typeof(TOut).GetPrivateConstructor();
if (ctor != null) // can replace
return Expression.New(ctor);
I will miss the arguments!
Update 2
If I use it as:
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;
}
The expression will be built, but will not be executed as it produces the following:
x => new MyType(MyProp = x.MyProp)
Which is incorrect again will produce the following error as expected:
Incorrect number of arguments for constructor
Assuming that MyType
looking like this
public class MyType
{
public MyType(string myProp)
{
MyProp = myProp;
}
public string MyProp { get; set; }
}
you can create a generic visitor:
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);
}
}
Usage:
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);
Some explanation:
In VisitNew
we check that constructor is belong to anonymous type. If so we trying to search in TOut
type for constructor with same arguments. On success we replace anonymous type constructor with constructor from TOut