【问题标题】:What is a good design when trying to build objects from a list of key value pairs?尝试从键值对列表构建对象时,什么是好的设计?
【发布时间】:2013-02-08 22:05:12
【问题描述】:

因此,如果我有一种方法可以解析文本文件并返回 list of list of 键值对,并且想从返回的 kvps 创建对象(每个 kvps 列表代表一个不同的对象),最好的方法是什么?

想到的第一个方法很简单,只要保留一个关键字列表:

private const string NAME   = "name";
private const string PREFIX = "prefix";

并检查我得到的键是否有上面定义的我想要的常量。这是我正在做的项目的一个相当核心的部分,所以我想把它做好;有没有人有任何更可靠的建议(并不是说上述方法本质上不可靠 - 我只是在四处打听)?

编辑:

已要求提供更多详细信息。我在业余时间做一个小游戏,我正在用配置文件构建游戏世界。有四个 - 一个定义所有生物,另一个定义所有区域(及其在地图中的位置),另一个定义所有对象,最后一个定义各种配置选项和不适合其他地方的东西。对于前三个配置文件,我将根据文件的内容创建对象——它会包含大量文本,因此会有很多字符串,比如名称、复数、前缀等。配置值都是这样的:

-
key: value 
key: value
-
key: value
key: value
-

“-”线表示一个新的部分/对象。

【问题讨论】:

    标签: c# key-value


    【解决方案1】:

    深入了解XmlSerializer。即使您被限制不使用磁盘上的 XML,您也可能想要复制它的一些功能。这可能看起来像这样:

    public class DataObject {
      [Column("name")]
      public string Name { get; set; }
    
      [Column("prefix")]
      public string Prefix { get; set; }
    }
    

    请注意在文件中包含某种格式版本,否则下一次格式更改时您将陷入地狱的厨房。

    【讨论】:

      【解决方案2】:

      做了很多无根据的假设,我认为最好的方法是创建一个工厂,它将接收键值对列表并返回正确的对象或在它无效时抛出异常(或创建一个虚拟对象,或在特定情况下更好)。

      private class Factory {
      
         public static IConfigurationObject Factory(List<string> keyValuePair) {
      
             switch (keyValuePair[0]) {
      
                case "x":
                    return new x(keyValuePair[1]);
                    break;
                /* etc. */
                default:
                    throw new ArgumentException("Wrong parameter in the file");
             }
      
        }
      
      }
      

      这里最强烈的假设是,您的所有对象都可以部分视为相同(即,它们实现相同的接口(示例中为 IConfigurationObject)或属于相同的继承树)。

      如果他们不这样做,那么这取决于您的程序流程以及您正在使用它们做什么。但尽管如此,他们应该:)

      编辑:根据您的解释,每种文件类型可以有一个工厂,其中的开关将是每种文件类型允许的类型的权威来源,并且它们可能有一些共同点。反思是可能的,但它的风险更大,因为它不像这个那么明显和自我记录。

      【讨论】:

        【解决方案3】:

        你需要对象做什么?按照您描述它的方式,无论如何您都会将它们用作某种(按键方式)受限映射。如果您不需要某种继承,我只需将类似地图的结构包装到这样的对象中:

        [java-inspired pseudo-code:]
        class RestrictedKVDataStore {
           const ALLOWED_KEYS = new Collection('name', 'prefix');
           Map data = new Map();
        
           void put(String key, Object value) {
              if (ALLOWED_KEYS.contains(key))
                  data.put(key, value)
           }
        
           Object get(String key) {
              return data.get(key);
           }
        }
        

        【讨论】:

          【解决方案4】:

          您可以创建一个与列名匹配的接口,然后使用 Reflection.Emit API 在运行时创建一个类型,以便访问字段中的数据。

          【讨论】:

            【解决方案5】:

            编辑:

            从头开始,这仍然适用,但我认为您所做的是读取配置文件并将其解析为:

            List<List<KeyValuePair<String,String>>> itemConfig = 
                new List<List<KeyValuePair<String,String>>>();
            

            在这种情况下,我们仍然可以使用反射工厂来实例化对象,我只需将嵌套的内部列表传递给它,而不是传递每个单独的键/值对。

            旧帖:

            这是一个使用反射的巧妙小方法:

            基本思路:

            • 为每个 Object 类使用一个公共基类。
            • 将所有这些类放在它们自己的程序集中。
            • 把这个工厂也放在那个组件中。
            • 传入您从配置中读取的 KeyValuePair,作为回报,它会找到与 KV.Key 匹配的类并使用 KV.Value 对其进行实例化
            公共类 KeyValueToObjectFactory { 私有字典 _kvTypes = new Dictionary(); 公共 KeyValueToObjectFactory() { // 将类型预加载到字典中,以便我们稍后查找 // 显然,你想重用工厂以最小化开销,所以不要 // 做一些愚蠢的事情,比如在循环中实例化一个新工厂。 foreach(在 typeof(KeyValueToObjectFactory).Assembly.GetTypes() 中输入类型) { if (type.IsSubclassOf(typeof(KVObjectBase))) { _kvTypes[type.Name.ToLower()] = type; } } } 公共 KVObjectBase CreateObjectFromKV(KeyValuePair kv) { 如果(千伏!=空) { 字符串 kvName = kv.Key; // 如果类型信息在我们的字典中,则实例化该类的新实例。 类型 kvType; if (_kvTypes.TryGetValue(kvName, out kvType)) { 返回 (KVObjectBase)Activator.CreateInstance(kvType, kv.Value); } 别的 { throw new ArgumentException("无法识别的 KV 对"); } } 别的 { 返回空值; } } }

            【讨论】:

              【解决方案6】:

              @大卫:
              我已经有了解析器(其中大部分都是手写的,所以我决定不使用 XML)。但这看起来我这样做的方式非常好;我得去看看。关于版本控制也很重要。

              @Argelbargel:
              那看起来也不错。 :')

              【讨论】:

                【解决方案7】:

                ...这是一个相当核心的部分 我正在做的项目...

                真的吗?

                很容易将其抽象出来并提供一个基本实现,以便以后重构。

                然后你可以继续做重要的事情:游戏。

                只是一个想法

                【讨论】:

                  【解决方案8】:

                  真的吗?

                  是的;我已经想到了这一点。我绝不会做不必要的工作。 :')

                  【讨论】:

                    猜你喜欢
                    • 1970-01-01
                    • 2019-10-05
                    • 1970-01-01
                    • 2021-11-04
                    • 2013-08-27
                    • 1970-01-01
                    • 2019-06-19
                    • 2021-06-04
                    • 1970-01-01
                    相关资源
                    最近更新 更多