Switch o dizionario quando si assegna a un nuovo oggetto

12

Recentemente, ho preferito preferire il mapping delle relazioni 1-1 utilizzando Dictionaries anziché Switch istruzioni. Trovo che sia un po 'più veloce da scrivere e più facile da elaborare mentalmente. Sfortunatamente, quando si effettua il mapping su una nuova istanza di un oggetto, non voglio definirlo in questo modo:

var fooDict = new Dictionary<int, IBigObject>()
{
    { 0, new Foo() }, // Creates an instance of Foo
    { 1, new Bar() }, // Creates an instance of Bar
    { 2, new Baz() }  // Creates an instance of Baz
}

var quux = fooDict[0]; // quux references Foo

Dato questo costrutto, ho sprecato cicli di CPU e memoria creando 3 oggetti, facendo qualunque cosa potessero contenere i loro costruttori, e ho finito solo usando uno di essi. Credo anche che mappare altri oggetti a fooDict[0] in questo caso causerà loro di fare riferimento alla stessa cosa, piuttosto che creare una nuova istanza di Foo come previsto. Una soluzione sarebbe usare invece una lambda:

var fooDict = new Dictionary<int, Func<IBigObject>>()
{
    { 0, () => new Foo() }, // Returns a new instance of Foo when invoked
    { 1, () => new Bar() }, // Ditto Bar
    { 2, () => new Baz() }  // Ditto Baz
}

var quux = fooDict[0](); // equivalent to saying 'var quux = new Foo();'

È arrivato ad un punto in cui è troppo confuso? È facile perdere quel () alla fine. O la mappatura a una funzione / espressione è una pratica abbastanza comune? L'alternativa sarebbe utilizzare un interruttore:

IBigObject quux;
switch(someInt)
{
    case 0: quux = new Foo(); break;
    case 1: quux = new Bar(); break;
    case 2: quux = new Baz(); break;
}

Quale invocazione è più accettabile?

  • Dizionario, per ricerche più veloci e meno parole chiave (caso e interruzione)
  • Switch: più comunemente trovato nel codice, non richiede l'uso di a FUNC < > oggetto per la ricerca indiretta.
posta KChaloux 18.10.2012 - 16:09
fonte

4 risposte

7

Questa è un'interpretazione interessante del schema di fabbrica . Mi piace la combinazione del dizionario e dell'espressione lambda; mi ha fatto guardare il contenitore in un modo nuovo.

Sto ignorando la preoccupazione nella tua domanda sui cicli della CPU come hai detto nei commenti che l'approccio non-lambda non fornisce quello che ti serve.

Penso che entrambi gli approcci (switch vs. Dictionary + lambda) andranno bene. L'unica limitazione è che utilizzando il dizionario, si limitano i tipi di input che si potrebbero ricevere per generare la classe restituita.

L'utilizzo di un'istruzione switch fornirebbe maggiore flessibilità sui parametri di input. Tuttavia, se questo dovesse essere un problema, potresti avvolgere il Dizionario all'interno di un metodo e ottenere lo stesso risultato finale.

Se è nuovo per la tua squadra, commenta il codice e spiega cosa sta succedendo. Richiedi una revisione del codice della squadra, guidali attraverso ciò che è stato fatto e rendili consapevoli di ciò. Oltre a questo, sembra a posto.

    
risposta data 18.10.2012 - 16:25
fonte
6

C # 4.0 ti offre la classe Lazy<T> , che è simile alla tua seconda soluzione, ma urla "Inizializzazione pigra" in modo più esplicito.

var fooDict = new Dictionary<int, Lazy<IBigObject>>()
{
    { 0, new Lazy(() => new Foo()) }, // Returns a new instance of Foo when invoked
    { 1, new Lazy(() => new Bar()) }, // Ditto Bar
    { 2, new Lazy(() => new Baz()) }  // Ditto Baz
}
    
risposta data 20.10.2012 - 13:08
fonte
2

Stilisticamente penso che la leggibilità sia uguale tra loro. È più facile fare l'iniezione di dipendenza con Dictionary .

Non dimenticare che è necessario verificare se la chiave esiste quando si utilizza Dictionary , e deve fornire un fallback se non lo fa.

Preferirei l'istruzione switch per i percorsi di codice statici e Dictionary per i percorsi di codice dinamici (in cui è possibile aggiungere o rimuovere voci). Il compilatore potrebbe essere in grado di eseguire alcune ottimizzazioni statiche con switch che non può con Dictionary .

È interessante notare che questo pattern Dictionary è ciò che le persone fanno a volte in Python, perché a Python manca l'istruzione switch . Altrimenti usano if-else chains.

    
risposta data 18.10.2012 - 16:44
fonte
1

In generale, preferirei nessuno dei due.

Qualunque cosa stia consumando dovrebbe funzionare con un Func<int, IBigObject> . Quindi la fonte della tua mappatura può essere un dizionario o un metodo che ha un'istruzione switch o una chiamata al servizio web o qualche ricerca di file ... qualsiasi cosa.

Per quanto riguarda l'implementazione, preferirei il dizionario poiché è più facilmente rifattorizzato da "dizionario del codice duro, chiave di ricerca, risultato restituito" a "carica dizionario da file, chiave di ricerca, risultato restituito".

    
risposta data 18.10.2012 - 16:26
fonte

Leggi altre domande sui tag