Доступ к индексатору из дерева выражений
Я работаю над функцией фильтрации. Фильтр будет представлять собой дерево выражений пользователя. Там будет около 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 Пункт