【问题标题】:Linq - How to query Dictionary<int,object>Linq - 如何查询 Dictionary<int,object>
【发布时间】:2019-07-06 16:32:35
【问题描述】:

我希望能够通过MyObj 中的任何属性来查询和排序Dictionary&lt;int, MyObj&gt;

class MyObj
  {
    public string Symbol { get; set; }
    public string Name { get; set; }
    public int AtomicNumber { get; set; }
    public int Id { get; set; }

    public string toString()
    {
      return Symbol + " " + Name + " " + AtomicNumber + " " + Id;
    }

  }

  class Program
  {

    private static void AddToDictionary(Dictionary<int, MyObj> elements,
    string symbol, string name, int atomicNumber, int id)
    {
      MyObj theElement = new MyObj();

      theElement.Symbol = symbol;
      theElement.Name = name;
      theElement.AtomicNumber = atomicNumber;
      theElement.Id = id;

      elements.Add(key: theElement.Id, value: theElement);
    }

    private static Dictionary<int, MyObj> BuildDictionary()
    {
      var elements = new Dictionary<int, MyObj>();

      AddToDictionary(elements, "K", "Potassium", 19, 0);
      AddToDictionary(elements, "Ca", "Calcium", 20, 1);
      AddToDictionary(elements, "Sc", "Scandium", 21, 2);
      AddToDictionary(elements, "Ti", "Titanium", 22, 3);

      return elements;
    }

    static List<T> GetListOfProperty<T>(Dictionary<int, MyObj> colBlobs, string property)
    {
      Type t = typeof(MyObj);
      PropertyInfo prop = t.GetProperty(property);
      if (prop == null)
      {
        // throw new Exception(string.Format("Property {0} not found", f.Name.ToLower()));
        Console.WriteLine(string.Format("Property {0} not found", property));
        return new List<T>();
      }
      //still need to fix this 
      return colBlobs.Values
          .Select(blob => (T)prop.GetValue(blob))
          .OrderBy(x => x)
          .ToList();
    }


    static SortedDictionary<int, MyObj> GetListOfProperty2<T>(Dictionary<int, MyObj> colBlobs, string property)
    {

      // CODE?

      return sortedDict;
    }


    static void Main(string[] args)
    {
      Dictionary<int, MyObj> myColl = BuildDictionary();

      var res = GetListOfProperty<string>(myColl, "Name");
      foreach (var rr in res)
        Console.WriteLine(rr.ToString());
      //outputs : Which is only one property, the one selected
      //--------
      //Calcium
      //Potassium
      //Scandium
      //Titanium

      var res2 = GetListOfProperty2<string>(myColl, "Name");
      //want to output the whole dictionary
      //<1, {"Ca", "Calcium", 20,1}
      //<0, {"K", "Potassium", 19, 0}
      //<2, {"Sc", "Scandium", 21, 2}
      //<3, {"Ti", "Titanium", 22, 3}
    }
  }

因为似乎不清楚我想要什么。我添加了示例输出。我很确定没有办法让这个问题更清楚。

【问题讨论】:

  • “失败”是什么意思?看起来该方法应该返回一个字典列表,但您试图只返回一个字典。我不明白您实际上要做什么。字典不是排序的集合。如果需要,请使用SortedDictionary
  • 这不行,因为“x”没有Key也没有Value。你的字典应该是什么样子的?你期望的键和期望值是多少?
  • 我看不出 `.Select(blob => (T)prop.GetValue(blob))` 有什么用处,因为 blob 的类型为 KeyValue&lt;int, MyObj&gt;,但您正在尝试获取MyObj的属性...
  • @unixsnob 您发布的代码在您声称它有效的情况下无法编译(“如果我返回列表它有效”)并按照@987654328 的预期抛出“对象与目标类型不匹配” @line 试图说服该代码至少运行时...
  • @HenrikHansen:嗨,我希望这足够清楚,您可以提交答案。我真的不明白为什么人们认为它如此不清楚。我只想通过对象内的属性来查询和排序字典。例如,为MyObj 的 AtomicNumber 属性 > 20 获取排序的 对。

标签: c# linq


【解决方案1】:

SortedDictionary 的问题在于它只能按Key 排序,所以你必须以某种方式使用OrderBy()

public static IOrderedEnumerable<KeyValuePair<K, T>> SortByMember<K, T>(this Dictionary<K, T> data, string memberName)
{
  Type type = typeof(T);
  MemberInfo info = type.GetProperty(memberName) ?? type.GetField(memberName) as MemberInfo;

  Func<KeyValuePair<K, T>, object> getter = kvp => kvp.Key;

  if (info is PropertyInfo pi)
    getter = kvp => pi.GetValue(kvp.Value);
  else if (info is FieldInfo fi)
    getter = kvp => fi.GetValue(kvp.Value);

  return data.OrderBy(getter);
}

这样可以同时处理属性和字段,如果成员名无效,则默认为key排序。

如果成员名称无效,您可以通过更改返回值将其更改为不排序:

public static IEnumerable<KeyValuePair<K, T>> SortByMember<K, T>(this Dictionary<K, T> data, string memberName)
{
  Type type = typeof(T);
  MemberInfo info = type.GetProperty(memberName) ?? type.GetField(memberName) as MemberInfo;

  if (info == null) return data;

  Func<KeyValuePair<K, T>, object> getter = null;

  if (info is PropertyInfo pi)
    getter = kvp => pi.GetValue(kvp.Value);
  else if (info is FieldInfo fi)
    getter = kvp => fi.GetValue(kvp.Value);

  return data.OrderBy(getter);
}

IMO 如果找不到成员,则返回空字典是错误的。或者,您可以抛出异常。

用例:

  Dictionary<int, MyObj> myColl = BuildDictionary();

  var res = myColl.SortByMember("Name");

  foreach (var rr in res)
    Console.WriteLine(rr.Value);

【讨论】:

  • 我同意返回空值并不理想,但在我的情况下,这是有道理的,因为我使用字典创建 UI 电子邮件以显示结果。
  • 嗯,这只是一个原型,并没有真正做实际的数据库工作。实际数据将来自某个外部系统。
【解决方案2】:

为了实现您的目标,您可以使用表达式树来构建编译的 lambda,然后在 Linq OrderBy() 方法中使用此 lambda:

public static class ExpressionHelper 
{
    public static Func<T, object> GetMemberExpressionFunc<T>(string memberName)
    {
        var parameter = Expression.Parameter(typeof(T));
        Expression source = Expression.PropertyOrField(parameter, memberName);  
        Expression conversion = Expression.Convert(source, typeof(object));
        return Expression.Lambda<Func<T, object>>(conversion, parameter).Compile();
    }
}   

static void Main(string[] args)
{
    Dictionary<int, MyObj> myColl = BuildDictionary();

    string property = "AtomicNumber";    // or whatever property you want your dictionary ordered by
    var func = ExpressionHelper.GetMemberExpressionFunc<MyObj>(property);
    var ordered = myColl.OrderBy(x => func(x.Value));
}

【讨论】:

  • 干净整洁。有理由使用Expression.Convert()吗?乍一看我没看到?
  • @HenrikHansen,如果没有Expression.Convert(),您可以按一些string 属性订购,但在其他情况下(例如,按int 属性订购)您会得到一个`ArgumentException
  • @HenrikHansen,您的解决方案更好。如果MyObj 有一个bool 属性,那么当我通过它订购字典时,我实际上会遇到异常,但您的解决方案在这种情况下效果很好。
  • @HenrikHansen,是的,bool 属性也很好。我只是在使用和不使用Expression.Convert() 的情况下编写了错误的代码
  • @unixsnob,你为什么不能像这样添加Where() 子句:var ordered = myColl.Where(x =&gt; x.Value.AtomicNumber &gt; 20).OrderBy(x =&gt; func(x.Value));
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多