Aquí hay un visitante que hace el trabajo.
- se hace una copia del parámetro (ya que vamos a necesitar para crear un nuevo parámetro y sustituir todas las referencias de la antigua parámetro para la nueva)
- que camina la
.Body
del árbol, sustituyendo la parámetro y cambiar cualquier miembro de acceso en contra de la vieja tipo a un miembro del mismo nombre del tipo nueva
- se re-ensambla una lambda utilizando el parámetro inventamos earler
Código:
class TypeChangeVisitor : ExpressionVisitor
{
private readonly Type from, to;
private readonly Dictionary<Expression, Expression> substitutions;
public TypeChangeVisitor(Type from, Type to, Dictionary<Expression, Expression> substitutions)
{
this.from = from;
this.to = to;
this.substitutions = substitutions;
}
public override Expression Visit(Expression node)
{ // general substitutions (for example, parameter swaps)
Expression found;
if(substitutions != null && substitutions.TryGetValue(node, out found))
{
return found;
}
return base.Visit(node);
}
protected override Expression VisitMember(MemberExpression node)
{ // if we see x.Name on the old type, substitute for new type
if (node.Member.DeclaringType == from)
{
return Expression.MakeMemberAccess(Visit(node.Expression),
to.GetMember(node.Member.Name, node.Member.MemberType,
BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Single());
}
return base.VisitMember(node);
}
}
public class Program
{
public static void Main()
{
Expression<Func<AnimalViewModel, bool>> predicate = x => x.Name == "abc";
var switched = Translate<AnimalViewModel, Animal>(predicate);
}
public static Expression<Func<TTo, bool>> Translate<TFrom, TTo>(Expression<Func<TFrom, bool>> expression)
{
var param = Expression.Parameter(typeof(TTo), expression.Parameters[0].Name);
var subst = new Dictionary<Expression, Expression> { { expression.Parameters[0], param } };
var visitor = new TypeChangeVisitor(typeof(TFrom), typeof(TTo), subst);
return Expression.Lambda<Func<TTo, bool>>(visitor.Visit(expression.Body), param);
}
}
Tenga en cuenta que si usted tiene x.Something.Name
puede que tenga que ser un poco más cuidadoso, pero esto se debe conseguir una manera razonable.
Por qué no funciona si usted deriva AnimalViewModel de una clase abstracta que tiene una propiedad pública? Aunque si se pone el mismo alojamiento hasta el AnimalViewModel y no en la clase abstracta, que funcionará – Agzam
@Agzam reemplazar DeclaringType con ReflectedType en ese caso –
No es ayudar a pesar de que – Agzam