【问题标题】:Dynamic linq: Is there a way to access object data by index?动态 linq:有没有办法通过索引访问对象数据?
【发布时间】:2012-01-28 02:59:44
【问题描述】:

我需要使用 Dynamic Linq 进行一些内存过滤。 我的对象只有一个索引器:

public  object this[int index] { }

对我的数据的访问是这样的:object[0], object[1],...

所以我的查询是这样的:

// get FilterText from user at runtime
// eg. filterText can be: [0] > 100 and [1] = "wpf"
collection.AsQueryable().where(filterText);

有没有办法做到这一点?

【问题讨论】:

    标签: c# casting dynamic-linq indexer


    【解决方案1】:

    我有一些消息要告诉你。

    这实际上是可能的......但是! (是的,有一个但是)。

    我假设您使用的是动态 Linq 库。然后,您必须使用“it”关键字来指定您希望对其应用索引操作的当前对象。

    但是...您的索引器的返回数据类型是对象,因此由于数据类型不匹配,您将无法写入 [0] > 100 和 [1] = "wpf"。

    此外,即使您从 DynamicObject 派生并在运行时添加属性,这些属性也不会在运行时由处于当前状态的动态 linq 库解析。您只会得到类型 xxx 中不存在的字段或属性。

    对此有多种解决方案,您可以接受其中一些作为解决方案。

    • 一个丑陋的解决方案,如果你的数据类型数量有限,比如 n (其中 n

      it[0] > 100 AND it[1, "dummy"] = "wpf" //The dummy parameter allows you to specify return type.
      
    • 另一个丑陋的解决方案,Dynamic Linq 支持使用 ToString() 和转换方法,因此,您可以例如编写 与上述相同的查询:

      Convert.ToDouble(it[0]) > 100 AND it[1].ToString() = "wpf".
      
    • 第三种丑陋的解决方案,您可以使用包装的约定 以一种告诉您如何转换数据的方式声明。为了 例如,你可以这样写:

      it[0] > 100 AND it{1} = "wpf"
      

      并将“it[”替换为“Convert.ToDouble(it[”等等...

    如果我是正确的,我认为您也不能在库中使用通用索引器。而 Convert.ChangeType 在这种情况下对你没有好处,因为返回类型仍然是对象。

    也许我或其他人会在一段时间内重写库以支持这些事情,但在不久的将来(几周)我没有时间这样做。

    好吧,我很抱歉,但我必须在 15 分钟内到达某个地方,所以我希望以后我们必须采取更好的解决方案!

    将自己传送到会议中

    更新:

    我想我可能已经找到了解决您(和我)问题的方法!

    在 LINQ 库中,您可以在 IxxxSignatures 接口中为您希望能够使用的类型添加一个成员。

    所以,我在 IEqualitySignatures 中添加了(例如):

    void F(Object x, Int32 y);
    

    并像这样修改了 else 块中的(在这种情况下)ParseComparison 方法。

    left = Expression.Convert(left, right.Type);
    

    而且,不管你信不信,它奏效了 :)

    我没有测试所有类型的操作,因为我没有添加其他类型和操作的签名,但是应该很简单!

    更新

    更新了上面的一些小东西..

    我正在对此进行更多试验,虽然它可能也不是最漂亮的解决方案,但您可以这样做(DataObject 只是一个带有索引器的 DynamicObject):

        [TestMethod]
        public void DynamicTest()
        {
            List<DataObject> dataObjects = new List<DataObject>();
    
            dynamic firstObject = new DataObject();
            dynamic secondObject = new DataObject();
    
            firstObject.dblProp = 10.0;
            firstObject.intProp = 8;
            firstObject.strProp = "Hello";
    
            secondObject.dblProp = 8.0;
            secondObject.intProp = 8;
            secondObject.strProp = "World";
    
            dataObjects.Add(firstObject);
            dataObjects.Add(secondObject);
    
            /* Notice the different types */
            string newQuery = FormatQuery("dblProp > 9.0 AND intProp = 8 AND strProp = 'Hello'");
    
            var result = dataObjects.Where(newQuery);
    
            Assert.AreEqual(result.Count(), 1);
            Assert.AreEqual(result.First(), firstObject);
        }
    

    还有某种格式方法,比如(假设我写了一个完整的方法):

        public string FormatQuery(string query)
        {
            query = query.Replace('\'', '\"');
    
            string[] operators = new string[] { "<", ">", "!=", "<=", ">=", "<>", "=" };
    
            string[] parts = query.Split();
    
            for (int i = 0; i < parts.Length; i++)
            {
                if (operators.Contains(parts[i]))
                {
                    parts[i - 1] = "it[\"" + parts[i - 1] + "\"]";
                }
            }
    
            return String.Join(" ", parts);
        }
    

    我们当然可以使用扩展方法。

    或者...我们可以将方法放在 LINQ 库中,使用类似(虽然不是一个好主意):

    if (typeof(T).GetInterface("IDynamicMetaObjectProvider", true) != null)
        whereClause = whereClause.FormatQuery();
    

    当然,检查该类型是否实现了字符串索引器,类似于(此处忽略 IndexerName 属性):

    if (t.GetType().GetProperty("Item") != null)
    

    这将使“普通用户”能够编写:

    data.Where("dblProp > 9.0 AND intProp = 8 AND strProp = 'Hello'")
    

    好吧,也许把这些东西放在那里并不好,但你明白了。你可以有! :)

    【讨论】:

    • 真的吗?我认为您不需要做那些丑陋的事情,请参阅我的回答。
    • 是的,我知道。我只是想说明类型是对象,因此某些操作在没有转换的情况下不适用。有一个隐式转换会很好。也许 LINQ 可以查看右侧的值并将左侧的值转换为它(我知道,它已经做了一些,但 libs 总是可以为你做更多的事情:P)。
    • 感谢您的出色回答。我现在了解背景和可能的情况。但我不是很满意:(。我今天试过了
    • 正如你所描述的问题是类型转换。我找不到动态更改类型的方法。也不是通过反射获得类型并转换它。所以下一步我将尝试从我的收藏中获取一个元素。与反射相比,我将使用索引器 this[n] 来获取所有元素并获取此元素的类型。在 switch 语句中,我可以添加转换方法。所以我得到了你的第二个例子。
    • 约翰,我更新了你关于格式想法的帖子。明天我会努力改进字体,如果可能的话!?并做一些测试。如果可能并且您对此感兴趣,我们可以交换我们的 LINQ。
    【解决方案2】:

    您要么使用 DynamicLinq http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx,要么使用反射构建表达式树。很多人为此更喜欢 Dynamic Linq 库;我自己没用过,我是用反射的方式。

    【讨论】:

    • 不确定是谁给了你-1。这是一个完全有效的答案。 +1
    • 谢谢,@EBarr。我想知道为什么即使它是正确的,我自己也会被否决。
    • 感谢您的评论。我使用 Dlinq 库。我现在将其扩展为将 [n] 转换为 it[n]。但问题是类型转换。请参阅我对其他陈述的回答。
    【解决方案3】:

    您只需在索引器前面加上it,这在DynamicLinq 语言中相当于C# 中的this

    所以,你只需要it[1] == "wpf"

    但是,由于您的索引器返回 object,因此会更加复杂。对于类类型(包括string),你很好,DLinq 将根据需要推广所有内容。

    但是,对于像ints 这样的值类型,您必须使用Int32(it[0]) &gt; 10

    【讨论】:

    • 不,它不是我所拥有的完整功能集[1].ToString()。在这种情况下,我也可以使用“=,>,
    • @Schorsch 抱歉,我不明白你的意思。你能解释一下吗? “问题是要获得正确的类型来添加这个方法。” - 得到正确的类型是什么?添加什么方法?
    • @Schorsch,我已经更新了我的答案。希望对你有用!
    • @Krizz 感谢您的想法。我的意思是问题是从我的模型中获取真实类型的值。因为 this[n] 或 it[n] 的结果是一个对象。但约翰有很好的解决方案来处理这个问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-11
    • 1970-01-01
    • 2018-11-08
    • 1970-01-01
    • 2016-03-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多