深度嵌套字典是反模式吗?
Posted
技术标签:
【中文标题】深度嵌套字典是反模式吗?【英文标题】:Is a deep nested Dictionary an antipattern? 【发布时间】:2012-03-07 09:54:49 【问题描述】:我有一个结构可以很容易地使用三层嵌套字典来表示,就像这样
private static Dictionary<string, Dictionary<string, Dictionary<string,string>>> PrerenderedTemplates;
这种结构可能会用到这样的地方
PrerenderedTemplates[instanceID][templategroup][templatepart]
现在,我意识到这段代码很难阅读,因为通过查看定义语句,您无法判断它的用途。将其更改为Dictionary<string, PrerenderedTemplate>
时我真正看到的唯一优势是可读性。将每个嵌套转换为自己的类(例如class PrerenderedTemplate class TemplateGroup class TemplatePart
)将添加更多代码行,但计算优势很小(如果有的话)。据我所知。
Dictionary
的工作原理
有处理这种嵌套的最佳做法吗?
请记住,这是一个私有成员,对于使用该类的人来说,它不需要直截了当。
更新
因此,受 Reza 的启发,但无法使用元组,我决定创建自己的密钥生成器并像这样实现他的模式:
private Dictionary<string, string> PrerenderedTemplates;
private string GetPrerenderedTemplateKey(string InstanceId, string FeatureId, string OptionId)
return new StringBuilder(instanceId)
.Append(FormatTools.LIST_ENTRY_DELIMITER)
.Append(templategroup)
.Append(FormatTools.LIST_ENTRY_DELIMITER)
.Append(templatepart).ToString();
其中FormatTools.LIST_ENTRY_DELIMITER
是Unicode 专用字符0xe04d
。
【问题讨论】:
由于前两个嵌套本质上只是标识符,也许我可以使用简单的 DictionaryPrerenderedTemplates["instance1"]["fruit"]["banana"]
可以只表示为PrerenderedTemplates["instance1_fruit_banana"]
,就像命名空间一样。PrerenderedTemplates
来列出您的模板组或模板部分?有点像PrerenderedTemplates[instanceID].Keys
或PrerenderedTemplates[instanceID][templateGroup]
?如果是这样,那么这可能是处理它的最简单方法。
@M.Babcock,好吧,我正在循环一个对象集合,这些对象包含指向如何呈现模板的元数据。在渲染该模板之前,我想检查我的字典以确保它之前没有被渲染过。如果没有,我会渲染它并将结果添加到我的字典中。 (模板变更管理在别处处理)
@Iain Fraser 我喜欢您的第一个选项,其中包含您不允许在键语言中作为分隔符的特殊字符。我过去做过,它对我有用。
类似***.com/questions/11908991/…, ***.com/questions/955982/…
【参考方案1】:
我提供另一种选择:
Dictionary<Tuple<string, string, string>, string> pt;
访问字典:
pt[Tuple.Create("id","group","part")]
C# 7 中引入的更新:
Value Tuples 最引人注目:
Dictionary<(string id, string group, string part), string> pt;
访问字典:
pt[("id", "group", "part")]
【讨论】:
很好,我一定会试一试,让你知道结果如何! 我不懂你的意思!! 对不起,Reza,我告诉过你我喜欢你的方法,我会尝试一下。我应该尽量不要使用这么多的俚语。 我应该指定我使用的是 C# v3.5,而 Tuple 仅在 C# v4.0 中引入。我也很想用你的模式:( 没有什么能阻止你将类似的类用作键 - 实现 Equals 和 GetHashCode ,你就可以开始了(添加其他东西,如 == 会很好,但可选,即示例***.com/questions/569903/multi-value-dictionary)。将键连接为字符串的更简洁的解决方案,也有点复杂。【参考方案2】:我会创建一个自定义字典。像这样的
public class TrippleKeyDict
private const string Separator = "<|>";
private Dictionary<string, string> _dict = new Dictionary<string, string>();
public string this[string key1, string key2, string key3]
get return _dict[GetKey(key1, key2, key3)];
set _dict[GetKey(key1, key2, key3)] = value;
public void Add(string key1, string key2, string key3, string value)
_dict.Add(GetKey(key1, key2, key3), value);
public bool TryGetValue(string key1, string key2, string key3, out string result)
return _dict.TryGetValue(GetKey(key1, key2, key3), out result);
private static string GetKey(string key1, string key2, string key3)
return String.Concat(key1, Separator, key2, Separator, key3);
如果您认为连接字符串不够安全,因为键可能包含分隔符,那么请使用您自己的键类型或 Touple<string,string,string>
作为键。由于此实现细节隐藏在您的自定义字典中,您可以随时更改它。
你可以像这样使用字典
var dict = new TrippleKeyDict();
// Using the Add method
dict.Add(instanceID, templategroup, templatepart, "some value");
// Using the indexer
dict[instanceID, templategroup, templatepart] = "xy";
string result = dict[instanceID, templategroup, templatepart];
// Using the TryGetValue method
if (dict.TryGetValue(instanceID, templategroup, templatepart, out result))
// Do something with result
【讨论】:
我明白你的意思。hello_world > hooray > stuff
和 hello > world_hooray > stuff
会发生冲突。两者都将使用密钥“hello_world_hooray_stuff”...
我使用"|"
作为分隔符。你可以使用另一个你知道它从未在你的密钥中使用过的,比如"<|>"
。
我正在构建的框架使用一个非常晦涩的 unicode 字符作为分隔符。敢说我会用! :)
哇,今天学到了:en.wikipedia.org/wiki/…
我不能使用 touples,因为我使用 .NET 3.5,但是将字典定义为 Dictionary<Touple<string,string,string>,string>
,正如 Reza Arab 所展示的那样,并将其合并到 TrippleKeyDict
中应该不会太难。
【参考方案3】:
我想提供一种替代方法,使用 SortedDictionary 和自定义比较器:
public class PrerenderedTemplate
public string instanceID;
public string templategroup;
public string templatepart;
public PrerenderedTemplate(string id, string tempGroup, string tempPart)
instanceID = id;
templategroup = tempGroup;
templatepart = tempPart;
// custom comparer instance used as argument
// to SortedDictionary constructor
public class Comparer : IComparer<PrerenderedTemplate>
public int Compare(PrerenderedTemplate x, PrerenderedTemplate y)
int compare = 0;
if (compare == 0) compare = x.instanceID.CompareTo(y.instanceID);
if (compare == 0) compare = x.templategroup.CompareTo(y.templategroup);
if (compare == 0) compare = x.templatepart.CompareTo(y.templatepart);
return compare;
这样使用:
var dictionary = new SortedDictionary<PrerenderedTemplate, string>(new PrerenderedTemplate.Comparer());
dictionary.Add(new PrerenderedTemplate("1", "2", "3"), "123");
dictionary.Add(new PrerenderedTemplate("4", "5", "6"), "456");
dictionary.Add(new PrerenderedTemplate("7", "8", "9"), "789");
Assert.AreEqual<string>(dictionary[new PrerenderedTemplate("7", "8", "9")], "789");
RezaArab 的回答是符合目的的,但我个人不喜欢元组,因为它们具有模棱两可的属性和冗长的语法。
如果任何需求发生变化,带有比较器的自定义类会提供更高的清晰度和灵活性。
【讨论】:
以上是关于深度嵌套字典是反模式吗?的主要内容,如果未能解决你的问题,请参考以下文章