Доступ к индексатору из дерева выражений - PullRequest
13 голосов
/ 20 июля 2011

Я работаю над функцией фильтрации. Фильтр будет построением дерева выражений пользователем. Там будет около 30 полей, которые пользователь может использовать для фильтрации. Я думаю, что лучший способ - создать объектную модель с индексатором и получить доступ к необходимым значениям по индексу типа enum.

См. Этот пример:

enum Field
{
    Name,
    Date,
}

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

Я хотел бы спросить, как я могу получить доступ к индексатору из дерева выражений.

Ответы [ 2 ]

16 голосов
/ 15 марта 2012

Я опубликую полный пример того, как использовать индексатор:

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 содержит непосредственно значение индексированного свойства.Для записи в него мы должны использовать Expression.Assign.Все остальное довольно ванильно Expression.Как пишет Даниэль, индексатор обычно называется «товаром».Обратите внимание, что Expression.Property имеет перегрузку, которая принимает непосредственно имя индексатора (поэтому "Item"), но я решил найти его вручную (чтобы его можно было использовать повторно).Я даже привел пример того, как использовать LINQ, чтобы найти точную перегрузку индексатора, которую вы хотите.

Как любопытство, если вы посмотрите на MSDN, например, для Dictionary , под Свойства вы найдете Предмет

16 голосов
/ 20 июля 2011

Индексатор - это простое свойство, обычно называемое Item. Это означает, что вы можете обращаться к индексатору, как и к любому другому свойству, используя его имя.

Имя свойства индексатора может быть изменено разработчиком класса с помощью атрибута IndexerName .

Чтобы надежно получить фактическое имя свойства индексатора, вы должны отразить класс и получить атрибут DefaultMember .
Более подробную информацию можно найти здесь .

...