深层嵌套字典是反模式吗?

发布于 2025-01-06 00:50:30 字数 1296 浏览 1 评论 0原文

我有一个可以很容易地使用三层嵌套字典来表示的结构,就像这样

private static Dictionary<string, Dictionary<string, Dictionary<string,string>>> PrerenderedTemplates;

结构可能会使用这样的地方

PrerenderedTemplates[instanceID][templategroup][templatepart]

现在,我意识到这段代码很难阅读,因为从定义语句来看,你无法说出它的用途。我真正看到的将其更改为 Dictionary 的唯一优点是可读性。将每个嵌套转换为它自己的类(例如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 Private使用字符0xe04d

I have a structure that can be very easily represented using a three-deep nested dictionary, like so

private static Dictionary<string, Dictionary<string, Dictionary<string,string>>> PrerenderedTemplates;

Where the structure might be used something like this

PrerenderedTemplates[instanceID][templategroup][templatepart]

Now, I realise that this code is hard to read, because from looking at the definition statement, you can't tell what it's being used for. The only advantage I can really see in changing it to Dictionary<string, PrerenderedTemplate> is readability. Converting each nesting into its own class (e.g class PrerenderedTemplate{} class TemplateGroup{} class TemplatePart{}) would add many more lines of code for little (if any) computational advantage. As far as I can see.

  • So, is my approach "ok" or should I go the extra mile and create seperate classes?
  • Is it okay to cover how the nested Dictionary works in the documentation/comments
  • Is there a best practice for handling this sort of nesting?
  • Bear in mind, this is a private member, it doesn't need to be straightforward for people using the class.

Update

So, inspired by Reza, but unable to use Tuples, I decided to create my own key generator and implement his pattern like this:

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();
}

Where FormatTools.LIST_ENTRY_DELIMITER is the Unicode Private Use Character 0xe04d.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

兔小萌 2025-01-13 00:50:30

我提供另一种选择:

Dictionary<Tuple<string, string, string>, string> pt;

访问字典:

pt[Tuple.Create("id","group","part")]

更新

C# 7中引入的值元组最引人注目:

Dictionary<(string id, string group, string part), string> pt;

访问字典:

pt[("id", "group", "part")]

I offer another choice:

Dictionary<Tuple<string, string, string>, string> pt;

Access to dictionary:

pt[Tuple.Create("id","group","part")]

UPDATE:

Value Tuples introduced in C# 7 is most eye-catching:

Dictionary<(string id, string group, string part), string> pt;

Access to dictionary:

pt[("id", "group", "part")]
初懵 2025-01-13 00:50:30

我会创建一个自定义词典。 如果您认为

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 作为键。由于此实现细节隐藏在您的自定义字典中,因此您可以随时更改它。

你可以像这样使用字典

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
}

I would create a custom dictionary. Something like this

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);
    }
}

If you think, concatenating the strings is not safe enough, because the keys could contain the separators, then use your own key type or a Touple<string,string,string> as key. Since this implementation detail is hidden inside your custom dictionary, you can change it at any time.

You can use the dictionary like this

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
}
仄言 2025-01-13 00:50:30

我想提供一种替代方法,使用 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 的答案适合目的,但我个人不喜欢 Tuples它们模糊的属性和冗长的语法的基础。

如果任何需求发生变化,带有比较器的自定义类可以提供更高的清晰度和灵活性。

I would like to offer an alternative approach, using a SortedDictionary and a custom comparer:

    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;
            }
        }
    }

Is used like so:

    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's answer is fit for purpose but personally I dislike Tuples on the basis of their ambiguous properties and verbose syntax.

A custom class with comparer offers more clarity and also flexibility, should any requirements change.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文