【问题标题】:How to get value from string expression如何从字符串表达式中获取值
【发布时间】:2013-09-26 22:19:34
【问题描述】:

我的数据库中存储了一个字符串:

"Users.ElementAt(1).LastName"

然后我有一个像这样的对象:

        MyClass myclass = new MyClass ()
        {
            Users = new List<User>()
            {
                new User()
                {
                    LastName = "LastName1"
                },
                new User()
                {
                    LastName = "LastName2"
                },
            }
        };  

有没有办法针对我的对象解析/评估/运行给定的字符串以获取每个用户的姓氏?

我一直在使用 DynamicLinq 库,但我遇到了 ElementAt(1) 的问题并显示错误消息:

“不存在适用的聚合方法'ElementAt'”

谁能在这里提供一些指导?我是否必须求助于编写自己的解析器并使用反射?

【问题讨论】:

标签: c# linq reflection dynamic-linq linq-expressions


【解决方案1】:

DynamicLinq 的表达式解析器似乎只支持标准 LINQ 运算符方法的子集:

interface IEnumerableSignatures
{
    void Where(bool predicate);
    void Any();
    void Any(bool predicate);
    void All(bool predicate);
    void Count();
    void Count(bool predicate);
    void Min(object selector);
    void Max(object selector);
    void Sum(int selector);
    void Sum(int? selector);
    void Sum(long selector);
    void Sum(long? selector);
    void Sum(float selector);
    void Sum(float? selector);
    void Sum(double selector);
    void Sum(double? selector);
    void Sum(decimal selector);
    void Sum(decimal? selector);
    void Average(int selector);
    void Average(int? selector);
    void Average(long selector);
    void Average(long? selector);
    void Average(float selector);
    void Average(float? selector);
    void Average(double selector);
    void Average(double? selector);
    void Average(decimal selector);
    void Average(decimal? selector);
    void Take(int count);
    void Union(IQueryable right);
    void OrderBy(LambdaExpression exp);
    void OrderByDescending(LambdaExpression exp);
}

如您所见,ElementAt 不在其中。好消息是简单地将void ElementAt(int index); 添加到IEnumerableSignatures 似乎工作得很好。也就是说,当然,假设您使用的 LINQ 查询提供程序将处理 ElementAt 运算符;完全有可能不会。

但是如果修改 DynamicLinq 源不是一个选项,那么不,核心 .NET Framework 或 LINQ 中没有工具可以解析和评估这些类型的表达式。还有其他选择(请参阅 ScriptCS,Roslyn),但我怀疑您正朝着试图解决任何实际问题的“矫枉过正”的解决方案漂移。

【讨论】:

  • 我在发布此内容之前将 ElementAt 添加到上面的集合中,但它不起作用,我可能需要再试一次。源集合是一个 List,它确实有 ElementAt。我不认为我们使用它的目的是矫枉过正。我们确实通过不同的解决方案解决了这个问题,但我们仍在研究这个问题。感谢您的帮助,这是迄今为止最好的答案,所以我将标记为正确。
  • IQueryable 也不声明 ElementAt()。 DynamicLinq 使用其内部的IEnumerableSignatures 接口作为一种参考图表,用于匹配已知的扩展方法。不过,这个名称具有误导性,因为我相信它只会在目标类型实现 IQueryable 时查找匹配项(而不是在它仅实现 IEnumerable 时才查找匹配项)。如果您使源集合成为实现 IQueryable 的类型(或提供在列表中调用 AsQueryable() 的包装器属性),它应该可以工作。我假设您已经根据帖子中的错误文本这样做了。我的错。
【解决方案2】:
var allLastNamesInList= Users.Select(x=> x.LastName).toList();

此语句将遍历列表并获取所有姓氏。您甚至可以使用 order by,以便按顺序获取姓氏。

这是您正在寻找的要求还是不同的要求?

【讨论】:

  • 是的,是字符串,不是对象,发错了,改了,请看区别。
  • 是的。上面的语句会给你列表List&lt;string&gt; allLastNamesInList= Users.Select(x=&gt; x.LastName).toList();中的所有姓氏,即使你放了var,它也会自动输入cast。两者都可以工作
【解决方案3】:

我不确定你是否真的需要动态 linq 来做你想做的事。根据我的经验,如果您使用 C# .NET 4.5 或 4.0,只要常规 System.Linq 应该处理查找位置索引,并且元素 at 应该可以正常工作。这是一个简单的控制台应用程序,应该可以正常工作:

using System;
using System.Collections.Generic;
using System.Linq;


public class User
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}


class Program
{
    public static List<User> Users;

    static void Main(string[] args)
    {
            Users = new List<User>
            {
                new User
                    {
                        FirstName = "Brett",
                        LastName = "M"
                    },
                new User
                    {
                        FirstName = "John",
                        LastName = "Doe"
                    },
            new User
                {
                    FirstName = "Sam",
                    LastName = "Dilat"
                }
            };

        Console.WriteLine("Should work as long as object is defined and instantiated");
        Console.WriteLine(Users.ElementAt(1).FirstName);


        Console.WriteLine("\nIterate Through List with Linq\n I use a string you can add to another list or whatever\n\n");

        string s = "";

        Users.ForEach(n => s += "Position: " + Users.IndexOf(n) + " " + n.LastName + Environment.NewLine);
        Console.WriteLine(s);

        Console.ReadLine();
    }

}

如果您只想通过该集合的属性在集合中查找特定对象,您可以使用“FindIndex”方法:

Console.WriteLine("Index of Dilat: " + Users.FindIndex(n => n.LastName.Equals("Dilat")));

编辑 9-27-13 以拆分字符串。

如果您想获取一个字符串并将其拆分为一个很好的可启动列表以进行迭代,您也可以这样做。您只需要找到分隔符。如果你没有,你将不得不使用一些更复杂的正则表达式魔法。

保持一切不变,但替换我的 MAIN 方法:

string split = "Brett,Same,John,Frank";

        List<string> stringList = split.Split(',').ToList();

        string s = "";

        stringList.ForEach(n => s += n + Environment.NewLine);

        s += "\n\nFor Observing Element at\n";

        s += "John is at: " + stringList.FindIndex(n => n.Equals("John"));

        s += "\n\nGetting Poco Objects assigned and listed\n";

        var list = stringList.Select(u => new User
            {
                FirstName = u,
                LastName = "Something"
            }).ToList();

        list.ForEach(n => s += "User: " + n.FirstName + " " + n.LastName + "At: " + list.IndexOf(n) + Environment.NewLine);

【讨论】:

  • 对不起,我的问题没有包含表达式周围的引号,我在那里编辑过。我没有实例,它是我需要应用于对象的字符串。
  • 将字符串拆分成一个新列表并遍历它,查看更新后的答案。
  • 这与我想要的相反。我没有用户名,我希望它们来自使用字符串的对象。我会在 .'s 上拆分它并用表达式调用每个。我遇到了 ElementAt() 的问题
  • 您说:“这是一个字符串,我需要将其应用于对象。”我展示了这个: Console.WriteLine("Dilat 的索引:" + Users.FindIndex(n => n.LastName.Equals("Dilat")));让某个索引处的成员知道一个名字。我展示了如何从字符串列表中添加新对象,或者从字符串中将其拆分出来。我现在不知道你到底想要什么。
  • 我没有像“Dilat”这样的用户名。我想动态调用从字符串解析的 FindIndex
猜你喜欢
  • 2020-04-24
  • 2016-07-14
  • 1970-01-01
  • 1970-01-01
  • 2017-06-07
  • 1970-01-01
  • 2016-10-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多