2011-07-14 10 views
7

Jeśli mam dwie prawie identyczne klasy Animal i AnimalViewModel oraz drzewo wyrażeń powiązane z modelem podglądu, jak mogę go przetłumaczyć na Animal?Jak mogę przetłumaczyć drzewo wyrażeń jednego typu na inny typ wyrażenia?

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

Jak Mogę przetłumaczyć Expression<Func<AnimalViewModel,bool>> do 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. 
} 

Odpowiedz

7

Oto gość, który wykonuje pracę.

  • to tworzy kopię parametru (ponieważ musimy utworzyć nowy parametr i zastąpić wszystkie odwołania starego parametru dla nowego)
  • to spacery .Body drzewa, zastępując parametr, a przełączanie żadnych członków dostępu przeciwko starego typu do członka jak nazwany na nowy typu
  • to ponownie montuje się za pomocą parametru lambda wynaleźliśmy earler

Kod:

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

Pamiętaj, że jeśli masz x.Something.Name może trzeba być nieco bardziej ostrożny, ale to powinno Ci rozsądny sposób.

+0

Dlaczego to nie działa, jeśli pobierasz AnimalViewModel z klasy abstrakcyjnej, która ma właściwość publiczną? Chociaż jeśli umieścisz tę samą właściwość w AnimalViewModel, a nie w klasie abstrakcyjnej, to zadziała – Agzam

+0

@Agzam zamień DeclaringType na ReflectedType w takim przypadku –

+0

Nie pomóżcie jednak, jeśli – Agzam

Powiązane problemy