2011-07-20 28 views
10

Filtre işlevi üzerinde çalışıyorum. Filtre, bir kullanıcı tarafından bir ifade ağacı oluşturulacaktır. Kullanıcının filtrelemek için kullanabileceği yaklaşık 30 alan olacaktır. En iyi yolun, dizin modelini nesne modelini oluşturmak ve enum türüne göre gerekli değerlere erişmek olduğunu düşünüyorum. Ben bir ifade ağacından indexleyici nasıl erişebileceğini sormak istiyorumİfade ağacından dizinleyiciye erişme

enum Field 
{ 
    Name, 
    Date, 
} 

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

:

bu örneğe bakın.

cevap

15

Dizin oluşturucu, normal olarak Item adı verilen basit bir özelliktir. Bu, dizinleyiciye ismini kullanarak başka herhangi bir mülk gibi erişebileceğiniz anlamına gelir.

Dizin oluşturucu özelliğinin adı, sınıfın uygulayıcısı tarafından IndexerName attribute aracılığıyla değiştirilebilir.

Dizin Oluşturucu özelliğinin güvenilir adını almak için, sınıfı yansıtmanız ve DefaultMember attribute numaralı telefonu edinmeniz gerekir.
Daha fazla bilgi here bulunabilir.

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

IndexExpression doğrudan endeksli özelliğinin değeri içeren dizinleyicideki gelen okumak için:

+0

* "Normalde" *, ne zaman değil mi bulacaksınız? Nasıl güvenilir bir şekilde alabilirsin? –

+4

Dizin oluşturucudaki 'IndexerName 'özniteliği kullanılarak değiştirilebilir. Dizin oluşturucu içeren sınıfı yansıtabilir ve indexer özelliğinin adını güvenilir şekilde almak için 'DefaultMember' özniteliğini alabilirsiniz. Daha fazla bilgi için [burada] (http://social.msdn.microsoft.com/Forums/en-US/vstscode/thread/60de101a-278d-4674-bc1a-0a04210d566c) adresine bakın. –

+0

Çok teşekkürler. Gerçekten çalışıyor - Expression.Property (parametre, "Öğe", Expression.Constant (...)) – Ondra

12

Ben indexleyici nasıl kullanılacağına ilişkin eksiksiz bir örnek göndeririz. Yazmak için Expression.Assign kullanmalıyız. Diğer her şey oldukça vanilya Expression. Daniel tarafından yazılan gibi, Indexer normalde "Öğe" olarak adlandırılır. Expression.Property'un, dizinleyicinin adını doğrudan kabul eden bir aşırı yüklenmeye sahip olduğunu unutmayın (bu nedenle "Item"), ancak onu manuel olarak bulmayı seçtim (böylece yeniden kullanılabilir). İstediğiniz dizinleyicinin aşırı yüklenmesini bulmak için LINQ kullanımı hakkında bir örnek bile verdim. Eğer Dictionary örneğin MSDN'deki bakarsak sadece merak olarak

, Properties altında sen Item