Является ли глубоко вложенный словарь антипаттерном? - PullRequest
17 голосов
/ 16 февраля 2012

У меня есть структура, которую очень легко представить с помощью вложенного словаря с тремя глубинами, например,

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

Где структура может быть использована примерно так

PrerenderedTemplates[instanceID][templategroup][templatepart]

Теперь я понимаю, что этот код трудно читать, потому что, глядя на оператор определения, вы не можете понять, для чего он используется. Единственное преимущество, которое я действительно вижу в изменении его на Dictionary<string, PrerenderedTemplate>, - это удобочитаемость. Преобразование каждого вложения в его собственный класс (например, class PrerenderedTemplate{} class TemplateGroup{} class TemplatePart{}) добавило бы намного больше строк кода для небольшого (если таковое имеется) вычислительного преимущества. Насколько я вижу.

  • Итак, мой подход "в порядке" или я должен пройти лишнюю милю и создать отдельные классы?
  • Можно ли в документации / комментариях
  • Есть ли лучший метод для обработки такого рода вложений?
  • Имейте в виду, что это частный участник, он не должен быть простым для людей, использующих класс.

Обновление

Итак, вдохновленный Резой, но не способный использовать кортежи, я решил создать свой собственный генератор ключей и реализовать его шаблон следующим образом:

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.

Ответы [ 3 ]

16 голосов
/ 16 февраля 2012

Я предлагаю другой выбор:

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")]
1 голос
/ 16 февраля 2012

Я бы создал собственный словарь.Примерно так:

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
}
0 голосов
/ 19 ноября 2013

Я хотел бы предложить альтернативный подход, используя 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 соответствует цели, но лично мне не нравятся кортежи из-за их неоднозначных свойств и подробного синтаксиса.

Пользовательский класс с компаратором обеспечивает большую ясность, а также гибкость при любых требованиях.изменить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...