2011-07-20 20 views
10

Pracuję nad funkcją filtrowania. Filtr będzie drzewem ekspresji budowanym przez użytkownika. Będzie około 30 pól, których użytkownik może użyć do filtrowania. Myślę, że najlepiej jest stworzyć model obiektowy z indeksem i uzyskać dostęp do wymaganych wartości przez indeks typu enum.Uzyskiwanie dostępu do indeksu z drzewa wyrażeń

Zobacz ten przykład:

enum Field 
{ 
    Name, 
    Date, 
} 

class ObjectModel 
{ 
    object this[Field Key] 
    { 
     get 
     { 
      //... 
      return xx; 
     } 
    } 
} 

chciałbym zapytać w jaki sposób mogę uzyskać dostęp do indeksowania z drzewa ekspresji.

Odpowiedz

15

Indeksator jest prostą właściwością, zwykle nazywaną Item. Oznacza to, że możesz uzyskać dostęp do indeksatora, jak każdej innej właściwości, używając jej nazwy.

Nazwa obiektu indeksującego może zostać zmieniona przez implementatora klasy za pomocą IndexerName attribute.

Aby niezawodnie uzyskać aktualną nazwę właściwości indeksu, należy zastanowić się nad klasą i uzyskać numer DefaultMember attribute.
Więcej informacji można znaleźć here.

+0

* „Normalnie” *, gdy nie jest ona? Jak mogłeś niezawodnie się z tym pogodzić? –

+4

Można go zmienić za pomocą atrybutu 'IndexerName' na indeksatorze. Możesz zastanowić się nad klasą zawierającą indeksator i pobrać atrybut 'DefaultMember', aby niezawodnie uzyskać nazwę właściwości indeksu. Aby uzyskać więcej informacji, zobacz [tutaj] (http://social.msdn.microsoft.com/Forums/en-US/vstscode/thread/60de101a-278d-4674-bc1a-0a04210d566c). –

+0

Wielkie dzięki. To naprawdę działa - Expression.Property (parametr, "Pozycja", Expression.Constant (...)) – Ondra

12

wyślę kompletny przykład jak używać podziałowe:

ParameterExpression dictExpr = Expression.Parameter(typeof(Dictionary<string, int>)); 
ParameterExpression keyExpr = Expression.Parameter(typeof(string)); 
ParameterExpression valueExpr = Expression.Parameter(typeof(int)); 

// Simple and direct. Should normally be enough 
// PropertyInfo indexer = dictExpr.Type.GetProperty("Item"); 

// Alternative, note that we could even look for the type of parameters, if there are indexer overloads. 
PropertyInfo indexer = (from p in dictExpr.Type.GetDefaultMembers().OfType<PropertyInfo>() 
         // This check is probably useless. You can't overload on return value in C#. 
         where p.PropertyType == typeof(int) 
         let q = p.GetIndexParameters() 
         // Here we can search for the exact overload. Length is the number of "parameters" of the indexer, and then we can check for their type. 
         where q.Length == 1 && q[0].ParameterType == typeof(string) 
         select p).Single(); 

IndexExpression indexExpr = Expression.Property(dictExpr, indexer, keyExpr); 

BinaryExpression assign = Expression.Assign(indexExpr, valueExpr); 

var lambdaSetter = Expression.Lambda<Action<Dictionary<string, int>, string, int>>(assign, dictExpr, keyExpr, valueExpr); 
var lambdaGetter = Expression.Lambda<Func<Dictionary<string, int>, string, int>>(indexExpr, dictExpr, keyExpr); 
var setter = lambdaSetter.Compile(); 
var getter = lambdaGetter.Compile(); 

var dict = new Dictionary<string, int>(); 
setter(dict, "MyKey", 2); 
var value = getter(dict, "MyKey"); 

odczytu z indekser IndexExpression zawiera bezpośrednio na wartość nieruchomości indeksowanej. Aby do niego pisać, musimy użyć Expression.Assign. Wszystko inne jest całkiem waniliowe Expression. Jak napisano przez Daniela, Indexer jest zwykle nazywany "Przedmiotem". Zauważ, że Expression.Property ma przeciążenie, które akceptuje bezpośrednio nazwę indeksera (czyli "Item"), ale wybrałem to ręcznie (aby mogło być ponownie użyte). Przedstawiłem nawet przykład użycia LINQ do znalezienia dokładnego przeciążenia indeksu, który chcesz.

tylko jako ciekawostkę, jeśli spojrzeć na MSDN na przykład dla Dictionary pod Properties znajdziesz Item

Powiązane problemy