2012-02-16 12 views
16

Mi hanno una struttura che può essere facilmente rappresentato utilizzando un dizionario nidificato tre profondo, in questo modoUn dizionario annidato in profondità è un antipattern?

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

Qualora la struttura potrebbe essere usato qualcosa di simile

PrerenderedTemplates[instanceID][templategroup][templatepart] 

Ora, mi rendo conto che questo codice è difficile da leggere, perché guardando la definizione di definizione, non si può dire per cosa viene usato. L'unico vantaggio che posso davvero vedere nel modificarlo in Dictionary<string, PrerenderedTemplate> è la leggibilità. La conversione di ogni nidificazione in una propria classe (ad esempio class PrerenderedTemplate{} class TemplateGroup{} class TemplatePart{}) aggiungerebbe molte più righe di codice per un vantaggio computazionale piccolo (se non nullo). Per quanto posso vedere.

  • Quindi, è il mio approccio "ok" o dovrei fare il miglio supplementare e creare classi separate?
  • Va bene illustrare come funziona il nidificato Dictionary nella documentazione/commenti
  • Esiste una procedura ottimale per gestire questo tipo di nidificazione?
  • Tenete a mente, questo è un membro privato, non deve essere semplice per le persone che usano la classe.

Aggiornamento

Così, ispirato da Reza, ma in grado di utilizzare tuple, ho deciso di creare il mio generatore di chiavi e mettere in atto il suo modello come questo:

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

Dove FormatTools.LIST_ENTRY_DELIMITER è il carattere di uso privato Unicode 0xe04d.

+0

Poiché i primi due raggruppamenti sono essenzialmente solo identificatori, forse potrei farla franca con un semplice dizionario . Quindi 'PrerenderedTemplates [" instance1 "] [" fruit "] [" banana "]' potrebbe essere semplicemente rappresentato come 'PrerenderedTemplates [" instance1_fruit_banana "]', come un namespace. –

+0

Hai bisogno della possibilità di usare 'Modelli prerenderati 'per elencare i gruppi di modelli o le parti del modello? Una specie di "PrerenderedTemplates [instanceID] .Keys" o "PrerenderedTemplates [instanceID] [templateGroup]'? Se è così, questo è probabilmente il modo più semplice per gestirlo. –

+0

@ M.Babcock, beh, sto collegando un insieme di oggetti che contengono metadati che puntano a come eseguire il rendering di un modello. Prima di mostrare quel modello, voglio controllare il mio dizionario per assicurarmi che non sia già stato sottoposto a rendering. In caso contrario, eseguo il rendering e aggiungo il risultato al mio dizionario. (La modifica del modello è gestita altrove) –

risposta

14

Offro un'altra scelta:

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

accesso al dizionario:

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

Nice, sicuramente darò un via e ti faccio sapere come funziona! –

+0

Non capisco il tuo significato !! –

+2

Mi dispiace Reza, ti stavo dicendo che mi piace il tuo approccio e ho intenzione di provarlo. Dovrei provare a non usare così tanto il gergo. –

1

vorrei creare un dizionario personalizzato. Qualcosa di simile

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

Se si pensa, concatenando le stringhe non è abbastanza sicuro, perché le chiavi potrebbero contenere i separatori, quindi utilizzare il proprio tipo di chiave o un Touple<string,string,string> come chiave. Poiché questo dettaglio di implementazione è nascosto all'interno del dizionario personalizzato, puoi modificarlo in qualsiasi momento.

È possibile utilizzare il dizionario come questo

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 
} 
+1

Capisco cosa intendi. Si verificherebbe uno scontro con 'hello_world> hooray> stuff' e' hello> world_hooray> stuff'. Entrambi userebbero la chiave "hello_world_hooray_stuff" ... –

+0

Ho usato '" | "' come separatore. Potresti usarne un altro da cui sai che non è mai usato nelle tue chiavi, come "<|>" '. –

+0

Il framework su cui sto costruendo usa un carattere unicode molto oscuro come delimitatore. Osare dire che lo userò! :) –

0

vorrei offrire un approccio alternativo, utilizzando un SortedDictionary e un operatore di confronto personalizzato:

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

Viene utilizzato in questo modo:

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

Reza La risposta di Arab è adatta allo scopo ma personalmente non amo Tuples sulla base delle loro proprietà ambigue e della sintassi dettagliata.

Una classe personalizzata con il comparatore offre maggiore chiarezza e flessibilità, nel caso in cui le esigenze cambino.

Problemi correlati