2011-07-14 16 views
7

Si tengo dos clases casi idénticos Animal y AnimalViewModel y un árbol de expresión relacionada con el modelo de vista, ¿cómo puedo traducirlo a Animal?¿Cómo puedo traducir un árbol de expresiones de un tipo a un tipo de expresión diferente?

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; } 
} 

Como puedo traducir una Expression<Func<AnimalViewModel,bool>>-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. 
} 

Respuesta

7

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.

+0

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

+0

@Agzam reemplazar DeclaringType con ReflectedType en ese caso –

+0

No es ayudar a pesar de que – Agzam

Cuestiones relacionadas