2013-05-07 6 views
7

Ich habe ein Wörterbuch, wo der Schlüssel ein Tupel ist, wo das erste Element ein Datum ist und das zweite Element eine Zeichenfolge ist. Ich möchte, dass das Wörterbuch zwischen Groß- und Kleinschreibung unterscheidet.Case Insensitive Dictionary mit Tuple Key

Ich weiß, dass, wenn der Schlüssel nur eine Zeichenfolge war ich StringComparer.OrdinalIgnoreCase als Parameter beim Deklarieren des Wörterbuchs übergeben konnte, aber dies scheint nicht zu funktionieren, wenn der Schlüssel ein Tupel ist.

Gibt es eine Möglichkeit, den StringComparer für das zweite Element des Tuple anzugeben?

Dank

Antwort

13

Verwenden this overload des Dictionary Konstruktor, mit dem Sie einen benutzerdefinierten Vergleich für die Schlüssel angeben kann. Sie würden diese begleiten mit Erstellen einer Klasse, die

IEqualityComparer<Tuple<string, DateTime>> 

implementiert, die wie folgt aussehen könnte:

class CustomEqualityComparer : IEqualityComparer<Tuple<string, DateTime>> 
{ 

    public bool Equals(Tuple<string, DateTime> lhs, Tuple<string, DateTime> rhs) 
    { 
     return 
      StringComparer.CurrentCultureIgnoreCase.Equals(lhs.Item1, rhs.Item1) 
     && lhs.Item2 == rhs.Item2; 
    } 


    public int GetHashCode(Tuple<string, DateTime> tuple) 
    { 
     return StringComparer.CurrentCultureIgnoreCase.GetHashCode(tuple.Item1) 
      ^tuple.Item2.GetHashCode(); 
    } 
} 

Es gibt kein Argument überprüft hier, also bitte behandeln Sie dies nicht als Produktionscode. Außerdem muss darauf geachtet werden, dass die Implementierungen Equals und GetHashCode die all-wichtige Bedingung erfüllen, dass , wenn zwei Tupel gleich sind, sie denselben Hash-Code haben müssen. Wenn Sie mit benutzerdefinierten Textvergleichen arbeiten, ist es leicht, Bugs einzuführen, wenn Sie nicht besonders vorsichtig sind: Wenn Sie zum Beispiel ToLowerInvariant anstelle von ToLower oben verwenden, wäre das ein Fehler (der allerdings für einige Zeit nicht auftauchen könnte).

+0

Mit dem gleichen StringComparer zu schaffen für die Gleichstellung zu überprüfen und für immer Der Hash-Code funktioniert normalerweise am besten, um die von Ihnen erwähnten Fehler zu vermeiden. Ich habe die Antwort mit einem vorgeschlagenen Fix bearbeitet; Bitte rückgängig machen, wenn es dir nicht gefällt. – dtb

+0

@ dtb: Definitiv wird nicht rückgängig machen, da dies eine Konsistenzverbesserung auch IMO ist. Vielen Dank! – Jon

1

Da die Vergleiche Groß- und Kleinschreibung sein werden, könnten Sie die toLower/toUpper Methode im String Seite verwenden, wenn die Tupel zu machen, und dann immer unteren oder oberen die Saiten werden Sie in den Tupeln verwendet haben retrive/Einträge im Wörterbuch vergleichen.

+1

Das Vergleichen von Strings durch Umwandeln in Groß- oder Kleinschreibung funktioniert in einigen Kulturen nicht ordnungsgemäß. Es ist besser, einen StringComparer wie StringComparer.CurrentCultureIgnoreCase zu verwenden – dtb

0

Ich brauchte diese in einem Dictionary<Tuple<>> Wrapper, so habe ich @ Jon ‚s Code, um eine generische Version

public class TupleEqualityComparer<T1, T2> : IEqualityComparer<Tuple<T1, T2>> 
{ 
    private IEqualityComparer<T1> comparer1; 
    private IEqualityComparer<T2> comparer2; 

    public TupleEqualityComparer(IEqualityComparer<T1> comparer1, IEqualityComparer<T2> comparer2) 
    { 
     this.comparer1 = comparer1 ?? EqualityComparer<T1>.Default; 
     this.comparer2 = comparer2 ?? EqualityComparer<T2>.Default; 
    } 

    public bool Equals(Tuple<T1, T2> lhs, Tuple<T1, T2> rhs) 
    { 
     return comparer1.Equals(lhs.Item1, rhs.Item1) && comparer2.Equals(lhs.Item2, rhs.Item2); 
    } 

    public int GetHashCode(Tuple<T1, T2> tuple) 
    { 
     return comparer1.GetHashCode(tuple.Item1)^comparer2.GetHashCode(tuple.Item2); 
    } 

} 

public class Dictionary<TKey1, TKey2, TValue> : Dictionary<Tuple<TKey1, TKey2>, TValue>() 
{ 
    public Dictionary() : base() { } 
    public Dictionary(IEqualityComparer<TKey1> comparer1, IEqualityComparer<TKey2> comparer2) : base(new TupleEqualityComparer<TKey1, Tkey2>(comparer1, comparer2) { } 

    public TValue this[TKey1 key1, TKey2 key2] 
    { 
     get { return base[Tuple.Create(key1, key2)]; } 
    } 

    public void Add(TKey1 key1, TKey2 key2, TValue value) 
    { 
     base.Add(Tuple.Create(key1, key2), value); 
    } 

    public bool ContainsKey(TKey1 key1, TKey2 key2) 
    { 
     return base.ContainsKey(Tuple.Create(key1, key2)); 
    } 

    public bool TryGetValue(TKey1 key1, TKey2 key2, out TValue value) 
    { 
     return base.TryGetValue(Tuple.Create(key1, key2), out value); 
    } 
} 

Nutzungs

var dict = new Dictionary<string, DateTime, int>(
    StringComparer.OrdinalIgnoreCase, null); 
dict.Add("value1", DateTime.Now, 123); 
Assert.IsTrue(dict.ContainsKey("VALUe1"));