IDictionary: rendere la ricerca Case Insensitive

Quando programmo l’utilizzo da parte mia di IDictionary è frequente specialmente per ridurre gli accessi allo strati dati generando una sorta di cache minimale nel codice per la chiamata in corso. Purtroppo alle volte capita di non trovare la corrispondenza della chiave e sappiamo benissimo quello che comporta. Nella migliore delle ipotesi si tratta di un nuovo accesso al database, ma in altre possiamo ricevere il valore in out di default o peggio (anche se forse è meglio) una bellissima Exception.

IDictionary: Case Insensitive

Ora veniamo al punto cruciale per capire meglio il tutto. Partiamo dalla definizione di un semplice IDictionary nel nostro codice.

1
2
public IDictionary<string, string> Items { get; set; } 
    = new SortedDictionary<string, string>();

Utilizzando Items come appena dichiarato dobbiamo stare molto attenti a come viene utilizzata la key in quanto la ricerca è case sensitive. Per capirci? La chiave “Test” e “test” per lui sono diverse. Personalmente -avendo avuto una formazione in ambiente Unix alle superiori- le reputo diverse anche io e quindi sono dalla sua parte.

Applicando una semplice modifica alla precedente riga di codice possiamo “risolvere” il tutto uniformando la ricerca della chiave senza preoccuparci di come è scritta evitando di seminare il codice di .ToLower() oppure di .ToUpper().

1
2
3
4
5
public IDictionary<string, string> Items { get; set; } 
    = new SortedDictionary<string, string>
    (
        StringComparer.InvariantCultureIgnoreCase
    );

In cosa consiste la modifica? Durante la creazione del nostro IDictionary andremo ad indicare anche il tipo di comparazione da effettuare sulla chiave. Nel nostro caso gli diremo di ignorare sia la culture che il minuscolo /maiuscolo.

SortedDictionary - docs & interface

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
//
// Summary:
//     Initializes a new instance of the System.Collections.Generic.SortedDictionary`2
//     class that is empty and uses the specified System.Collections.Generic.IComparer`1
//     implementation to compare keys.
//
// Parameters:
//   comparer:
//     The System.Collections.Generic.IComparer`1 implementation to use when comparing
//     keys, or null to use the default System.Collections.Generic.Comparer`1 for the
//     type of the key.
public SortedDictionary(IComparer<TKey>? comparer);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//
// Summary:
//     Defines a method that a type implements to compare two objects.
//
// Type parameters:
//   T:
//     The type of objects to compare.
public interface IComparer<in T>
{
    //
    // Summary:
    //     Compares two objects and returns a value indicating whether one is less than,
    //     equal to, or greater than the other.
    //
    // Parameters:
    //   x:
    //     The first object to compare.
    //
    //   y:
    //     The second object to compare.
    //
    // Returns:
    //     A signed integer that indicates the relative values of x and y, as shown in the
    //     following table. Value Meaning Less than zero x is less than y. Zero x equals
    //     y. Greater than zero x is greater than y.
    int Compare([AllowNull] T x, [AllowNull] T y);
}

Dictionary - docs & interface

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//
// Summary:
//     Initializes a new instance of the System.Collections.Generic.Dictionary`2 class
//     that is empty, has the default initial capacity, and uses the specified System.Collections.Generic.IEqualityComparer`1.
//
// Parameters:
//   comparer:
//     The System.Collections.Generic.IEqualityComparer`1 implementation to use when
//     comparing keys, or null to use the default System.Collections.Generic.EqualityComparer`1
//     for the type of the key.
public Dictionary(IEqualityComparer<TKey>? comparer);
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//
// Summary:
//     Defines methods to support the comparison of objects for equality.
//
// Type parameters:
//   T:
//     The type of objects to compare.
public interface IEqualityComparer<in T>
{
    //
    // Summary:
    //     Determines whether the specified objects are equal.
    //
    // Parameters:
    //   x:
    //     The first object of type T to compare.
    //
    //   y:
    //     The second object of type T to compare.
    //
    // Returns:
    //     true if the specified objects are equal; otherwise, false.
    bool Equals([AllowNull] T x, [AllowNull] T y);
    //
    // Summary:
    //     Returns a hash code for the specified object.
    //
    // Parameters:
    //   obj:
    //     The System.Object for which a hash code is to be returned.
    //
    // Returns:
    //     A hash code for the specified object.
    //
    // Exceptions:
    //   T:System.ArgumentNullException:
    //     The type of obj is a reference type and obj is null.
    int GetHashCode([DisallowNull] T obj);
}