c# - ordenar - ¿Cómo puedo traducir un árbol de expresiones de un tipo a un tipo de expresión diferente?
metodo preorden c# (2)
Si tengo dos clases casi idénticas Animal
y AnimalViewModel
y un árbol de expresión relacionado con el modelo de vista, ¿cómo puedo traducirlo a Animal
?
public class Animal
{
public string Species { get; set; }
public string Name { get; set; }
public string Sound { get; set; }
}
public class AnimalViewModel : ViewModelBase
{
public string Species { get; set; }
public string Name { get; set; }
public string Sound { get; set; }
}
¿Cómo puedo traducir una Expression<Func<AnimalViewModel,bool>>
a Expression<Func<Animal,bool>>
?
public static Expression<Func<Animal,bool>> Translate (Expression<Func<AnimalViewModel,bool>> expression)
{
// What goes here? I assume I have to traverse the tree somehow.
}
Aquí hay un visitante que hace el trabajo.
- hace una copia del parámetro (ya que necesitaremos crear un nuevo parámetro y sustituir todas las referencias del parámetro anterior por el nuevo)
-
.Body
el.Body
del árbol, sustituye el parámetro y cambia cualquier acceso de miembro por el tipo anterior a un miembro de nombre similar en el nuevo tipo - reensambla un lambda usando el parámetro que hemos inventado 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 tiene x.Something.Name
es posible que deba ser un poco más cuidadoso, pero esto debería hacerlo de una manera razonable.
¿Has probado Automapper para algo como esto?