2011-07-20 13 views
10

Estoy trabajando en una función de filtrado. El filtro será un árbol de expresiones creado por un usuario. Habrá aproximadamente 30 campos que el usuario puede usar para filtrar. Creo que la mejor manera es crear el modelo de objetos con indexador y acceder a los valores requeridos por índice de tipo enum.Accediendo al indizador desde el árbol de expresiones

ver este ejemplo:

enum Field 
{ 
    Name, 
    Date, 
} 

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

me gustaría preguntar cómo puedo acceder a un indexador de un árbol de expresión.

Respuesta

15

El indexador es una propiedad simple, normalmente llamada Item. Esto significa que puede acceder al indexador como cualquier otra propiedad usando su nombre.

El nombre de la propiedad del indexador puede ser cambiado por el implementador de la clase mediante el IndexerName attribute.

Para obtener de manera confiable el nombre real de la propiedad del indexador, debe reflejar la clase y obtener el DefaultMember attribute.
Más información se puede encontrar here.

+0

* "normalmente" *, cuando no es así? ¿Cómo podría llegar de manera confiable? –

+4

Se puede cambiar usando el atributo 'IndexerName' en el indexador. Puede reflejar en la clase que contiene el indexador y recuperar el atributo 'DefaultMember' para obtener de manera confiable el nombre de la propiedad del indexador. Consulte [aquí] (http://social.msdn.microsoft.com/Forums/en-US/vstscode/thread/60de101a-278d-4674-bc1a-0a04210d566c) para obtener más información. –

+0

Muchas gracias. Realmente funciona - Expression.Property (parámetro, "Item", Expression.Constant (...)) – Ondra

12

Voy a publicar un ejemplo completo de cómo usar un indexador:

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

para leer desde el indexador la IndexExpression contiene directamente el valor de la propiedad indexada. Para escribirle, debemos usar Expression.Assign. Todo lo demás es bastante vanidoso Expression. Según lo escrito por Daniel, el Indizador normalmente se llama "Artículo". Tenga en cuenta que Expression.Property tiene una sobrecarga que acepta directamente el nombre del indexador (por lo que "Item"), pero elegí buscarlo manualmente (para poder reutilizarlo). Incluso he puesto un ejemplo sobre cómo usar LINQ para encontrar la sobrecarga exacta del indexador que desea.

Así como una curiosidad, si nos fijamos en MSDN por ejemplo para Dictionary, bajo Properties encontrará Item

Cuestiones relacionadas