【问题标题】:Accessing indexer from expression tree从表达式树访问索引器
【发布时间】:2011-07-20 09:02:34
【问题描述】:

我正在研究过滤功能。过滤器将是用户构建的表达式树。用户可以使用大约 30 个字段进行过滤。我认为最好的方法是使用索引器创建对象模型并通过枚举类型的索引访问所需的值。

看这个例子:

enum Field
{
    Name,
    Date,
}

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

我想问一下如何从表达式树中访问索引器。

【问题讨论】:

    标签: c# expression-trees


    【解决方案1】:

    我将发布一个关于如何使用索引器的完整示例:

    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。正如 Daniel 所写,索引器通常称为“项目”。请注意,Expression.Property 有一个重载,它直接接受索引器的名称(所以 "Item"),但我选择手动查找它(以便可以重复使用)。我什至举了一个例子,说明如何使用 LINQ 找到你想要的索引器的确切重载。

    出于好奇,如果你在 MSDN 上查看 Dictionary,在 Properties 下你会找到 Item

    【讨论】:

      【解决方案2】:

      索引器是一个简单的属性,通常称为Item。这意味着,您可以像访问任何其他属性一样使用索引器的名称来访问索引器。

      类的实现者可以通过IndexerName attribute 更改索引器属性的名称。

      要可靠地获取索引器属性的实际名称,您必须对类进行反思并获取DefaultMember attribute
      更多信息可以在here找到。

      【讨论】:

      • “通常”,不是吗?你怎么能可靠地做到这一点?
      • 可以使用索引器上的IndexerName 属性进行更改。您可以反映包含索引器的类并检索DefaultMember 属性以可靠地获取索引器属性的名称。请参阅here 了解更多信息。
      • 非常感谢。它确实有效 - Expression.Property(parameter, "Item", Expression.Constant(...))
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-13
      • 1970-01-01
      • 2011-11-06
      相关资源
      最近更新 更多