这更适合使用键值存储还是树形结构?

3
我正在尝试找出最佳的方式来表示一些数据。它基本上遵循制造商.产品.属性=值的形式,例如:
Acme.*.MinimumPrice = 100 Acme.ProductA.MinimumPrice = 50 Acme.ProductB.MinimumPrice = 60 Acme.ProductC.DefaultColor = Blue 因此,所有Acme产品的最低价格都是100,除了A和B产品。我想在C#中存储这些数据,并编写一些函数,使得GetValue("Acme.ProductC.MinimumPrice")返回100,而GetValue("Acme.ProductA.MinimumPrice")返回50。
我不确定如何最好地表示这些数据。在C#中有没有一种简洁的编码方式?
编辑:我可能没有表述清楚。这是需要存储在文本文件中的配置数据,然后以某种方式解析并存储在内存中,以便可以像我给出的示例那样检索。
4个回答

2

请按照以下格式精确地编写文本文件:

Acme.*.MinimumPrice = 100
Acme.ProductA.MinimumPrice = 50
Acme.ProductB.MinimumPrice = 60
Acme.ProductC.DefaultColor = Blue

将其解析为路径/值对序列:

foreach (var pair in File.ReadAllLines(configFileName)
                         .Select(l => l.Split('='))
                         .Select(a => new { Path = a[0], Value = a[1] }))
{
    // do something with each pair.Path and pair.Value
}

现在,你想要做的事情有两种可能的解释。字符串Acme.*.MinimumPrice可能意味着对于任何查找,如果没有特定的覆盖,例如Acme.Toadstool.MinimumPrice,我们返回100 - 即使文件中没有任何关于Toadstool的引用。或者它可能意味着只有在文件中有其他特定提及Toadstool时才应该返回100
如果是前者,您可以将整个内容存储在一个平面字典中,在查找时尝试不同的键变体,直到找到匹配的内容为止。
如果是后者,您需要构建一个数据结构来记录实际出现在路径结构中的所有名称,以避免返回不存在的值。这对我来说似乎更可靠。
因此,选择后者,Acme.*.MinimumPrice实际上是在说“将此MinimumPrice值添加到没有自己特别定义值的任何产品中”。这意味着你可以在解析时间处理这些键值对,消除所有星号,并将其扩展成配置文件的已完成版本的等效形式:
Acme.ProductA.MinimumPrice = 50
Acme.ProductB.MinimumPrice = 60
Acme.ProductC.DefaultColor = Blue
Acme.ProductC.MinimumPrice = 100

这种方法的好处是,你只需要一个扁平的字典作为最终表示,并且可以使用TryGetValue[]来查找内容。结果可能会更大,但这完全取决于你的配置文件有多大。
你可以以更小的方式存储信息,但我建议从简单的东西开始,给它一个非常简单的API,这样如果确实需要,稍后可以重新实现它。根据应用程序,你可能会发现使查找过程更复杂总体上更糟糕。

0

我不太确定你在问什么,但听起来你是在说:

我需要一个函数,它将为每个产品ID返回一个固定值100,除了两种情况:ProductA和ProductB

在这种情况下,您甚至不需要数据结构。一个简单的比较函数就可以做到。

int GetValue(string key) { 
  if ( key == "Acme.ProductA.MinimumPrice" ) { return 50; }
  else if (key == "Acme.ProductB.MinimumPrice") { return 60; }
  else { return 100; }
}

或者你可能一直在问

我需要一个函数,如果已定义,则返回一个值,如果没有,则返回100

在这种情况下,我会使用一个 Dictionary<string,int>。例如

class DataBucket {
  private Dictionary<string,int> _priceMap = new Dictionary<string,int>();
  public DataBucket() {
    _priceMap["Acme.ProductA.MinimumPrice"] = 50;
    _priceMap["Acme.ProductB.MinimumPrice"] = 60;
  }   
  public int GetValue(string key) { 
    int price = 0;
    if ( !_priceMap.TryGetValue(key, out price)) {
      price = 100;
    }
    return price;
  }
}

我不想硬编码任何内容。因此,第二个答案几乎是好的,除了100也会存储在字典中。 - JC.
@JC,我不确定为什么你在原问题含糊不清的情况下给我点了踩。 - JaredPar

0

其中一种方法是 - 您可以创建嵌套字典:Dictionary<string,Dictionary<string,Dictionary<string,object>>>。在您的代码中,您应该通过点拆分“Acme.ProductA.MinimumPrice”,并获取或设置与拆分块对应的字典的值。

另一种方法是使用Linq2Xml:您可以创建一个带有Acme作为根节点的XDocument,产品作为根节点的子节点,并且属性实际上可以存储为产品的属性或子节点。我更喜欢第二种解决方案,但如果您有数千个产品,则速度会较慢。


0
我会采用面向对象编程的方法来处理这个问题。你所解释的方式是将所有产品表示为对象,这是很好的。这似乎是多态性的一个很好的应用。
我会让所有的产品都有一个ProductBase,它有一个默认的虚拟属性。
virtual MinimumPrice { get { return 100; } }

然后您的特定产品,例如ProductA将覆盖功能:

override MinimumPrice { get { return 50; } }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接