Доступ к индексатору из дерева выражений

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

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

enum Field
{
    Name,
    Date,
}

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

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

Ответ 1

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

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

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

Ответ 2

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

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, например, на Словарь, в разделе Свойства вы, la find Пункт