2010-06-10 4 views
6

Ich möchte testen, ob eine ID noch nicht bekannt war oder, falls bekannt, ob sich der zugehörige Wert geändert hat. Ich verwende derzeit Code ähnlich, aber es ist schwer zu verstehen für diejenigen, die nicht mit dem Muster vertraut sind. Können Sie sich einen Weg vorstellen, um es lesbarer zu machen, während Sie es im LOC kurz halten?Wie kann ich diesen Wörterbuch-TryGetValue-Code lesbarer machen?

string id; 
string actual; 
string stored; 

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

Antwort

1

Es sieht gut zu mir ... liest so einfach wie jede andere 2 Bedingung if-Anweisung. Über das einzige, was ich vielleicht ist ändern würde die Negationen für einen frühzeitigen Ausstieg zu kippen:

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

ich keine Verwirrung in es überhaupt nicht sehen, die noch nie als besonders störend Idiom daran gedacht, und Demut suggerieren, dass sich diese von ihm verwirrten C# Entwickler daran gewöhnen. Es ist üblich, succint und gibt so viele LOC an das Problem, wie es verdient. Drehen Sie es in 10 Zeilen Code macht es Weg zu wichtig.

Wenn ich es oft verwendete, wäre eine Extension-Methode namens etwa ContainsEqualValue geeignet - aber ich würde den exakt gleichen Code in der Erweiterungsmethode verwenden, wie Sie haben.

+2

Es ist nicht ganz dasselbe wie jede 2-Bedingung-Anweisung, insofern es die Vordeklaration des 'out'-Parameters erfordert. "…an etwas gewöhnen?" Es ist nicht so, dass Entwickler "per se" "verwirrt" sind. Selbst ein erfahrener Programmierer, der von links nach rechts liest, muss hin- und herspringen, nur um das zu lesen, und dabei verschiedene Elemente im Kopf jonglieren. Dies ist keine * schwere * Aufgabe, aber es ist auch nicht selbstverständlich. Dies ist ein etwas prozeduraler Code und führt Möglichkeiten für Wiederholungen und Fehler ein. Lesbarkeit ist ein legitimes Problem und 'TryGetValue' mit einem' bool' Ergebnis ist semantisch schwach. – Jay

+0

@Jay: Warum sollten Sie die Deklaration der out-Variablen überprüfen? Es ist offensichtlich da (oder der Compiler würde sich beschweren), und Sie sollten sich nicht kümmern, was der Wert vorher war (da es ein out-Parameter ist, wird der Wert nicht von der Funktion verwendet). Geht es wirklich nur um "out" anstatt um bedingte? Über die einzigen Punkte, die ich zu dieser Aussage zugestehen würde, gehören 1.) Nebenwirkungen sind im Konditional und 2.) die Bedingungen sind auftragsabhängig. 'bool Try * (out)' ist eine gut etablierte Redewendung in der BCL, von der ich erwarte, dass die Entwickler ranken, also vergebe ich die erste. –

+0

@Jay: Ihr Kommentar ist "Spot-on". Meiner Meinung nach betrifft keine der Antworten die Lesbarkeit. Deshalb habe ich meinen vorgeschlagenen Code beigetragen. – AMissico

4

Also würde ich es wahrscheinlich zerlegen und ihm aussagekräftige Namen geben. Dies ist mehr zu lesen, aber Sie brauchen nicht viel zu sagen in Kommentaren:

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

Es wird initialisiert, das 'out'-Schlüsselwort in Kombination mit den Vorrangregeln des Operators sorgt dafür. – mafu

+0

Ein out-Parameter muss innerhalb der Funktion gesetzt werden, damit TryGetValue ihn initialisiert. –

+1

'gespeichert' wird immer nach dem Aufruf von' TryGetValue' initialisiert. Das ist die Regel eines Out-Parameters. Offensichtlich ist es kein gültiger Wert. – leppie

2

Duality.

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

Nicht sicher, ob es zwar lesbarer ist ... aber es ist gut zu wissen.

+2

Ich denke, das ist schwerer zu lesen als das Original. – mafu

+0

-1 für nicht lesbar. Ich hätte Schwierigkeiten, diese Aussagen zu verstehen und zu korrigieren. Vor allem, weil "gespeichert" ist "out", dann verwenden Sie es in der gleichen Aussage. Sie zwingen mich, jedes Element zu lesen, um zu verstehen, was vor sich geht. – AMissico

+0

@AMissico: Fallstricke der prozeduralen Code :) – leppie

3

jeden Teil der || in seine eigenen Methode oder Eigenschaft, als Sie es wie diese

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

Ich würde es vermeiden, viele Zeilen Code zu schreiben. – mafu

+2

Ich würde keine neuen Methoden erstellen, außer sie werden mögliche Fehler eliminieren. Ich würde neue Methoden erstellen, wenn es Lesbarkeit und Wartung erhöht. – AMissico

1

ich eine neue Methode würde es vorziehen, schreiben:

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

dann in der bestehenden Methode würde ich nur:

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

'AddValue' ist ein schrecklicher Name für eine Methode, die nichts tut/mutet. 'CanAddValue' könnte ein besserer Name sein. – leppie

+0

Schöne Idee. Vielleicht eher eine Erweiterungsmethode für Dictionary? Und die Benennung ist noch seltsam. – mafu

+0

stimmte der Benennung war seltsam. änderte es.Und ja, Sie könnten es als eine Erweiterungsmethode für das Wörterbuch implementieren, die es wahrscheinlich ein bisschen schöner machen würde ... –

5

Sie können eine Erweiterungsmethode mit einem guten Namen schreiben:

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

so später können Sie

string id; 
string actual; 

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

+1. Ich hasse "out" -Parameter, und ich schließe im Allgemeinen jeden Zugriff auf TryGetValue mit einer Erweiterungsmethode (zum Beispiel eine TryGetValueOrDefault, um den Eintrag direkt zurückgeben, oder Null oder 0, wenn es den Eintrag nicht finden kann). –

+0

"out" = unklarer Code – AMissico

0

Eine Erweiterung Methode wäre glatt verwenden:

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

Verbrauch:

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

Wenn Sie meinen, dass Sie immer wieder, dies zu tun haben, und es ist lang und hässlich, abstrahiere die Logik einer anderen Klasse und verwende eine Erweiterungsmethode.

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

Jetzt Ihren Code ersetzen:

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

Zu viel Code. Zu komplex für etwas so Einfaches. – AMissico

+0

@AMissico "Zu viel Code" ist subjektiv. Es hängt von der Domain und den Nutzungsbedürfnissen ab. Dies wird nur als ein Ansatz angeboten, der die Lesbarkeit in der Verwendung begünstigt und nur dann sinnvoll wäre, wenn dies an vielen Stellen eine Überprüfung wäre. Die Lesbarkeit verschlechtert sich mit zunehmender Anzahl von Parametern; Dies ist eine Möglichkeit, diese Verschlechterung abzuschwächen, wenn Sie dabei Wert sehen. – Jay

+0

Ich stimme vollkommen mit Ihnen überein, weshalb ich nicht abgestimmt habe. Ich bevorzuge "schlank", so dass Ihr Ansatz nur ein wenig schwer erscheint, weil Sie zehn Zeilen Code hinzugefügt haben, ihn nicht lesbarer gemacht haben und die verwirrende Aussage nicht geändert haben. – 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

Während ich erkennen, dass die „versuchen“ Muster notwendig ist, ich mag nicht Implementierungen, die eine „out“ Parameter erfordern. Es wäre viel nützlicher Funktionen haben scheinen ähnlich wie TryGetValue:

  • TryGetDictValue (Wörterbuch, key) gibt null zurück, wenn Schlüssel nicht im Wörterbuch ist
  • TryGetDictValue (Wörterbuch, Schlüssel, default) liefert default wenn Schlüssel nicht in Wörterbuch
  • TryGetDictValue (Wörterbuch, Schlüssel, valueReturningDelegate) ruft die mitgelieferte Delegat, wenn Schlüssel nicht im Wörterbuch ist und gibt das Ergebnis

in jedem Fall wird der Rückgabetyp des Ergebnisses, dass der Wörterbuch der Daten wäre.

Es ist schade, dass es keine Möglichkeit gibt, sich in eine Zeitmaschine zu schleichen und solche Dinge zu Methoden des Wörterbuchs zu machen. Auf der anderen Seite könnte man sie als statische Funktionen implementieren, indem man ein Wörterbuch als ersten Parameter nimmt.

+0

Dies könnte immer noch als Erweiterungsmethode gemacht werden, was Sie vielleicht mit Ihrem letzten Kommentar meinen. – Dykam