【问题标题】:How to dynamically convert List<MyClass> to List<KeyValue>?如何将 List<MyClass> 动态转换为 List<KeyValue>?
【发布时间】:2016-05-01 18:04:32
【问题描述】:

例如,如果我有这个类

public class User{
    int Id;
    string Name;
    short Age;
}

还有User的集合:

public class UserCollection: List<User>{...}

我可以从UserCollection 选择哪个属性是Key(或Value)来创建List&lt;KeyValue&gt;KeyValue 是我的班级)吗?

我尝试使用此代码:

private List<KeyValue> ReadCollection(dynamic collection, string keyName, string valueName)
{
    List<KeyValue> list = new List<KeyValue>();

    for(int i = 0; i < collection.Count; i++)
    {
        dynamic elem = collection[i];
        // I can't use this notation, but only elem.Id
        int key = elem[keyName]; 
        // I can't use this notation, but only elem.Name
        string value = elem[valueName];

        list.Add(new KeyValue(key, value));
     }

     return list;
}

List<KeyValue> list = ReadCollection(userCollection, "Id", "Name");

但它不起作用。可以选择将哪个属性放入我的KeyValue 类中?

【问题讨论】:

    标签: c# .net c#-4.0 dynamic


    【解决方案1】:

    好吧,由于源集合不是键值集合,您不能使用[] 符号来访问属性。这就是符号不起作用的原因。您可以尝试使用 GetProperty 之类的反射方法来动态访问该值:

    var key = elem.GetType().GetProperty(keyName).GetValue(elem, null);
    var value = elem.GetType().GetProperty(valueName).GetValue(elem, null);
    

    【讨论】:

      【解决方案2】:

      我至少会让方法通用,而不是采用动态对象。

      然后您可以生成简单的 LINQ 表达式来访问您需要的属性,并使用这些来获取每个条目的键和值:

      private static List<KeyValue> ReadCollection<T>(IList<T> collection, string keyName, string valueName)
      {       
          List<KeyValue> list = new List<KeyValue>();
      
          Func<T, int> keyFunction = GenerateLambda<T, int>(keyName);
          Func<T, string> valueFunction = GenerateLambda<T, string>(valueName);
      
          for (int i = 0; i < collection.Count; i++)
          {
              T elem = collection[i];
              int key = keyFunction(elem);
              string value = valueFunction(elem);
              list.Add(new KeyValue(key, value));
          }
          return list;
      
          // or simpler
          //return collection.Select(elem => new KeyValue(keyFunction(elem), valueFunction(elem))).ToList();
      }
      
      private static Func<T, TProp> GenerateLambda<T, TProp>(string propertyName)
      {
          var p = Expression.Parameter(typeof(T));
          var expr = Expression.Lambda<Func<T, TProp>>(Expression.PropertyOrField(p, propertyName), p);
          return expr.Compile();
      }
      
      List<KeyValue> list = ReadCollection(userCollection, "Id", "Name");
      

      Working .NET Fiddle here

      【讨论】:

      • 基本上你只是在做 Select 已经做的事情,除了这很容易出错。如果我传入“ID”而不是“Id”,或者更糟糕的是,我将“Name”作为第一个参数传入,“Id”作为第二个参数传入会怎样。不要误会我的意思,当用户选择键字段并选择值字段时需要完全动态的东西时,这是一个很好的解决方案,但它需要大量类型和错误检查以确保用户不会弄乱起来。
      • @StephenBrickner 是的,你是对的,它很容易出错。这就是允许用户选择他们想要/使用您的程序的原因。我只是在回答 OP 提出的基本问题,即当属性名称为 unknown 时如何生成列表。欢迎您发布包含这些额外要求的另一个答案。
      【解决方案3】:

      我会用Func 重写这个,而不是那些动态的东西。

      private List<KeyValue> ReadCollection<T>(IEnumerable<T> collection, Func<T, int> keyFunc, Func<T, string> valueFunc)
      {
      
          List<KeyValue> list = new List<KeyValue>();
      
          foreach (T elem in collection)
          {
              int key = keyFunc(elem);
              string value = valueFunc(elem);
      
              list.Add(new KeyValue(key, value));
           }
      
           return list;
      }
      

      你可以这样称呼它:

      List<KeyValue> list = ReadCollection<User>(userCollection, u => u.Id, u => u.Name);
      

      好处:

      • 所有代码都是键入的,所以没有运行时异常;
      • 您拥有完整的 IntelliSense 支持;
      • 它几乎支持任何集合类型,也支持 LINQ 枚举。

      【讨论】:

        【解决方案4】:

        您只需使用 Select 即可更简单地解决此问题。

        List<KeyValue> list = userCollection
                                 .Select(x => new KeyValue(x.ID, x.Name))
                                 .ToList();
        

        【讨论】:

        • 这是最好的方法,但不是很动态 - 如果他们想为键/值使用不同的属性怎么办?
        • 然后你会选择它们。传入“Id”、“Name”与在上面的键值对中选择它们是一样的。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-09-21
        • 1970-01-01
        • 1970-01-01
        • 2021-11-11
        • 1970-01-01
        相关资源
        最近更新 更多