3

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

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

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

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

编辑:

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

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

其中“-”行表示一个新的部分/对象。

4

8 回答 8

3

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

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

  [Column("prefix")]
  public string Prefix { get; set; }
}

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

于 2008-09-08T14:14:55.820 回答
3

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

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)或属于相同的继承树)。

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

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

于 2008-09-08T14:20:17.290 回答
2

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

[受java启发的伪代码:]
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);
   }
}
于 2008-09-08T14:16:00.077 回答
1

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

于 2008-09-08T14:08:05.760 回答
1

编辑:

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

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 对");
                }
            }
            别的
            {
                返回空值;
            }
        }
    }
于 2008-09-08T15:07:58.783 回答
0

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

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

于 2008-09-08T14:16:58.967 回答
0

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

真的吗?

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

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

只是一个想法

<bb />

于 2008-09-08T14:57:52.053 回答
0

真的吗?

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

于 2008-09-08T15:00:10.097 回答