2010-06-10 19 views
6

Vorrei provare se un ID non era ancora noto o, se è noto, se il valore associato è cambiato. Attualmente sto usando un codice simile a questo, ma è difficile da capire per chi non ha familiarità con il modello. Riesci a pensare a un modo per renderlo più leggibile senza tenerlo a LOC?Come posso rendere più leggibile questo codice TryGetValue del dizionario?

string id; 
string actual; 
string stored; 

if (!someDictionary.TryGetValue (id, out stored) || stored != actual) { 
    // id not known yet or associated value changed. 
} 

risposta

1

Si guarda bene a me ... recita facile come qualsiasi altra condizione di 2 if. L'unica cosa che vorrei cambiare forse è quello di capovolgere le negazioni per una rapida uscita:

if (someDictionary.TryGetValue(id, out stored) && stored == actual) { 
    return; 
} 
// store new value 

non vedo qualsiasi confusione a tutti, non hanno mai pensato a come un linguaggio particolarmente fastidioso, e umilmente suggerisco che quegli sviluppatori di C# confusi da esso si abituino ad esso. È comune, succint e fornisce il numero di LOC al problema che merita. Trasformarlo in 10 righe di codice lo rende così importante come modo.

Se l'ho usato spesso, un metodo di estensione chiamato qualcosa come ContainsEqualValue sarebbe appropriato - ma io userei lo stesso codice esatto nel metodo di estensione come avete.

+2

Non è esattamente la stessa cosa di qualsiasi istruzione a due condizioni in quanto richiede la pre-dichiarazione del parametro 'out'. "... ci si abitua?" Non è che gli sviluppatori siano "confusi", * di per sé *. Anche un veterano programmatore che legge da sinistra a destra deve rimbalzare avanti, indietro e in alto solo per leggere questo, e manipolare diversi elementi nella mente mentre lo fa. Questo non è un compito * difficile *, ma non è nemmeno naturale. Questo è in qualche modo un codice procedurale e introduce opportunità per la ripetizione e l'errore. La leggibilità è una preoccupazione legittima e 'TryGetValue' con un risultato' bool' è semanticamente debole. – Jay

+0

@Jay: Perché dovresti controllare la dichiarazione della variabile out? Ovviamente c'è (o il compilatore si lamenterebbe), e non dovresti preoccuparti di quale fosse il valore precedente (dato che è un parametro out, il valore non è usato dalla funzione). Si tratta davvero di "out" invece del condizionale? Circa gli unici punti che ammetterei su questa affermazione è che 1.) gli effetti collaterali sono condizionali, e 2.) i condizionali dipendono dall'ordine. 'bool Try * (out)' è un idioma ben consolidato nel BCL che mi aspetto che gli sviluppatori siano interessati, quindi perdono il primo. –

+0

@ Jay: il tuo commento è "azzeccato". A mio parere, nessuna delle risposte risolve la leggibilità. Questo è il motivo per cui ho assegnato il mio codice suggerito. – AMissico

4

Quindi probabilmente lo suddividerei e darei nomi significativi. Questo è più da leggere, ma non c'è bisogno di molto da dire nei commenti:

bool isKnown = someDictionary.TryGetValue (id, out stored); 
// can only change when it is known 
bool valueChanged = isKnown && stored != actual; 

// quite self-explanatory, isn't it? 
if (!isKnown || valueChanged) 
{ 

} 
+0

È inizializzato, la parola chiave 'out' in combinazione con le regole di precedenza degli operatori assicura che. – mafu

+0

Un parametro out deve essere impostato all'interno della funzione, in modo che TryGetValue lo inizializzi. –

+1

'stored' è sempre inizializzato dopo la chiamata a' TryGetValue'. Questa è la regola di un parametro 'out'. Ovviamente, non è un valore valido. – leppie

2

Dualità.

if (!(someDictionary.TryGetValue (id, out stored) && stored == actual)) ... 

Non so se è più leggibile se ... ma è bene sapere.

+2

Penso che sia più difficile da leggere rispetto all'originale. – mafu

+0

-1 per non leggibile. Avrei difficoltà a capire e correggere queste affermazioni. Soprattutto perché 'stored' è' out', quindi lo si utilizza nella stessa istruzione. Mi stai costringendo a leggere ogni elemento per capire cosa sta succedendo. – AMissico

+0

@AMissico: insidie ​​del codice procedurale :) – leppie

3

avvolgere ogni parte del || in proprio metodo o proprietà, che si può scrivere in questo modo

if (IdIsNew() || IdChanged()) 
+0

Preferirei evitare di scrivere molte righe di codice. – mafu

+2

Non creerei nuovi metodi a meno che non eliminino eventuali errori. Creerei nuovi metodi se aumentasse la leggibilità e la manutenzione. – AMissico

1

Preferirei un nuovo metodo:

public bool ShouldSetValue(Dictionary someDictionary, object id,object actualValue) 
{ 
    string stored; 

    if (someDictionary.TryGetValue (id, out stored)) 
    { 
     if (stored != actualValue) 
      return true; 
    } 
    else 
    { 
     return true; 
    } 
} 

poi nel metodo esistente Vorrei solo:

if (ShouldSetValue(someDictionary,id,actual)) 
{ 
    someDictionary[id]=actual; 
} 
+2

'AddValue' è un nome orribile per metodo che in realtà non esegue/modifica nulla. 'CanAddValue' potrebbe essere un nome migliore. – leppie

+0

Bella idea. Forse piuttosto un metodo di estensione per Dizionario? E il nome è ancora strano. – mafu

+0

concordare la denominazione era strano. cambiato.E sì, potresti implementarlo come metodo di estensione sul dizionario, il che probabilmente renderà un po 'più bello ... –

5

È possibile scrivere un metodo di estensione con un buon nome:

public static class Utility 
{ 
    public static bool ValueChangedOrUnknown(this Dictionary<string, string> dictionary, string id, string actual) 
    { 
     string stored = null; 
     return (!dictionary.TryGetValue(id, out actual) || stored != actual); 
    } 
} 

così più tardi è possibile utilizzare

string id; 
string actual; 

if (someDictionary.ValueChangedOrUnknown(id, actual) { 
    // id not known yet or associated value changed. 
} 
+1

+1. Detesto i parametri "out" e in genere ricompo qualsiasi accesso a TryGetValue con un metodo di estensione (ad esempio TryGetValueOrDefault per restituire direttamente la voce o null o 0 se non riesce a trovare la voce). –

+0

"out" = codice non chiaro – AMissico

0

un metodo di estensione sarebbe chiazza di petrolio:

public static class DictionaryExtensions 
{ 
    public static bool ShouldAddValue<TKey, TValue>(this Dictionary<TKey, TValue> someDictionary, TKey id, TValue actual) 
    { 
     TValue stored; 
     return (!someDictionary.TryGetValue(id, out stored) || !stored.Equals(actual)); 
    } 
} 

Usage:

someDictionary.ShouldAddValue("foo", "bar") 
0

Se vuoi dire che si deve fare questo più volte, ed è è lunga e brutta, astratta la logica di un'altra classe e usa un metodo di estensione.

public static class DictionaryExtensions 
{ 
    public static DictionaryChecker<TKey,TValue> contains<TKey,TValue>(this IDictionary<TKey,TValue> dictionary, TValue value) 
    { 
     return new DictionaryChecker<TKey,TValue>(value, dictionary); 
    } 
} 

public class DictionaryChecker<TKey,TValue> 
{ 
    TValue value; 
    IDictionary<TKey,TValue> dictionary; 

    internal DictionaryChecker(TValue value, IDictionary<TKey, TValue> dictionary) 
    { 
     this.value = value; 
     this.dictionary = dictionary; 
    } 

    public bool For(TKey key) 
    { 
     TValue result; 
     return dictionary.TryGetValue(key, out result) && result.Equals(value); 
    } 
} 

oggi sostituiscono il codice con:

if(!someDictionary.contains(actual).For(id)){ 
    // id not known yet or associated value changed. 
} 
+2

Troppo codice. Troppo complesso per qualcosa di così semplice. – AMissico

+0

@AMissico "Troppo codice" è soggettivo. Dipende dal dominio e dalle esigenze di utilizzo. Questo è appena offerto come un approccio che favorisce la leggibilità nell'uso, e avrebbe senso solo se si trattasse di un controllo che doveva essere fatto in molti posti. La leggibilità diminuisce all'aumentare del numero di parametri; questo è un modo per mitigare tale degrado, se si vede il valore nel farlo. – Jay

+0

Sono completamente d'accordo con te, motivo per cui non ho votato. Io preferisco "lean", quindi il tuo approccio sembra un po 'pesante perché hai aggiunto dieci righe di codice, non lo rendi più leggibile e non hai cambiato la dichiarazione confusa. – AMissico

0
public T GetValue(int id, object actual) 
{ 
    object stored; 
if (someDictionary.TryGetValue (id, out stored) || stored == actual) 
    return stored; 
    return new object(); 
} 
0

Mentre riconosco che il modello "try" è necessario, non mi piacciono le implementazioni che richiedono un parametro "out". Sembrerebbe molto più utile avere funzioni simili a TryGetValue:

  • TryGetDictValue (dizionario, key) restituisce null se la chiave non è nel dizionario
  • TryGetDictValue (dizionario, chiave, defaultValue) restituisce defaultValue se la chiave non è in dizionario
  • TryGetDictValue (dizionario, chiave, valueReturningDelegate) invoca il delegato in dotazione se la chiave non è nel dizionario e restituisce il suo risultato

in ogni caso, il tipo di ritorno del risultato sarebbe che dei dati del dizionario.

È un peccato che non ci sia modo di intrufolarsi in una macchina del tempo e fare in modo che tali metodi siano metodi di dizionario. D'altra parte, si potrebbero implementarli come funzioni statiche prendendo un dizionario come primo parametro.

+0

Questo potrebbe ancora essere fatto come metodo di estensione, che è forse quello che intendi con il tuo ultimo commento. – Dykam

Problemi correlati