Comment obtenir null au lieu de KeyNotFoundException accédant à la valeur Dictionary par clé?

Dans certains cas de figure, il m’a semblé utile d’avoir un moyen lisible et à court terme pour obtenir la valeur null au lieu de KeyNotFoundException lors de l’access à la valeur du dictionnaire par clé, lorsqu’il n’y en a pas dans le dictionnaire.

La première chose qui me vint à l’esprit était une méthode d’extension:

 public static U GetValueByKeyOrNull(this Dictionary dict, T key) where U : class //it's acceptable for me to have this constraint { if (dict.ContainsKey(key)) return dict[key]; else //it could be default(U) to use without U class constraint //however, I didn't need this. return null; } 

Mais ce n’est pas très court, en fait, quand vous écrivez quelque chose comme ceci:

 ssortingng.Format("{0}:{1};{2}:{3}", dict.GetValueByKeyOrNull("key1"), dict.GetValueByKeyOrNull("key2"), dict.GetValueByKeyOrNull("key3"), dict.GetValueByKeyOrNull("key4")); 

Je dirais que ce serait bien mieux d’avoir quelque chose de proche de la syntaxe de base: dict["key4"] .

Ensuite, j’ai eu l’idée d’avoir une classe avec un champ de dictionnaire private , qui exposait les fonctionnalités dont j’avais besoin:

 public class MyDictionary //here I may add any of interfaces, implemented //by dictionary itself to get an opportunity to, //say, use foreach, etc. and implement them // using the dictionary field. where U : class { private Dictionary dict; public MyDictionary() { dict = new Dictionary(); } public U this[T key] { get { if (dict.ContainsKey(key)) return dict[key]; else return null; } set { dict[key] = value; } } } 

Mais le léger changement dans le comportement de base semble un peu trop lourd.

Une autre solution pourrait consister à définir un Func dans le contexte actuel, comme ceci:

 Func GetDictValueByKeyOrNull = (key) => { if (dict.ContainsKey(key)) return dict[key]; else return null; }; 

il pourrait donc être utilisé comme GetDictValueByKeyOrNull("key1") .

Pourriez-vous, s’il vous plaît, me donner d’autres suggestions ou aider à en choisir une meilleure?

Voici ma solution de ma bibliothèque personnelle, implémentée comme méthode d’extension. Je ne fais que l’afficher car il est implémenté depuis l’ interface du dictionnaire et permet de transmettre une valeur par défaut facultative.

la mise en oeuvre

 public static TV GetValue(this IDictionary dict, TK key, TV defaultValue = default(TV)) { TV value; return dict.TryGetValue(key, out value) ? value : defaultValue; } 

Usage

  MyDictionary.GetValue("key1"); MyDictionary.GetValue("key2", -1); MyDictionary.GetValue("key3")?.SomeMethod(); 

Vous ne pouvez pas obtenir la syntaxe de votre choix avec une méthode d’extension, et comme d’autres l’ont dit, le remplacement d’une méthode / d’un opérateur pour modifier son comportement n’est généralement pas une bonne idée. Je pense que le mieux que vous puissiez faire est de raccourcir le nom que vous utilisez.

C’est si vous devez garder l’interface IDictionary. Si vous n’interférez avec aucun code qui attend un IDictionary, vous êtes libre de définir votre propre interface et le fait que l’opérateur [] fonctionne différemment n’est pas un problème.

Quoi que vous finissiez à appeler la fonction, vous voudrez l’implémenter comme ceci:

 public static U Get(this Dictionary dict, T key) where U : class { U val; dict.TryGetValue(key, out val); return val; } 

Il ne fait qu’une recherche, comparé à 2 pour vos implémentations.

À la fin, j’ai trouvé une variante utilisant une dérivation de la classe du dictionnaire avec une implémentation d’interface explicite:

 public interface INullValueDictionary where U : class { U this[T key] { get; } } public class NullValueDictionary : Dictionary, INullValueDictionary where U : class { U INullValueDictionary.this[T key] { get { U val; dict.TryGet(key, out val); return val; } } } 

Donc, il expose la fonctionnalité dont j’ai besoin de la manière suivante:

 //create some dictionary NullValueDictionary dict = new NullValueDictionary { {1,"one"} }; //have a reference to the interface INullValueDictionary idict = dict; try { //this throws an exception, as the base class implementation is utilized Console.WriteLine(dict[2] ?? "null"); } catch { } //this prints null, as the explicit interface implementation //in the derived class is used Console.WriteLine(idict[2] ?? "null"); 

Ajouter une classe DictionaryExtension

 public static class DictionaryExtension { public static TValue GetValueOrDefault ( this IDictionary dictionary,TKey key) { TValue value; return dictionary.TryGetValue(key, out value) ? value : default(TValue); } } 

Et il peut retourner la valeur par défaut si pas trouvé clé dans le dictionnaire.

Le paramètre par défaut est null s’il s’agit d’un type de référence.

 _dic.GetValueOrDefault(); 

Je prémisse en disant que je ne l’ utiliserais pas . Le new mot-clé, bien qu’utile dans ce cas, peut créer des bogues vraiment difficiles à trouver. Sinon, vous pouvez essayer cette classe.

 class MyDictionary : Dictionary { public new TValue this[TKey key] { get { TValue value; return TryGetValue(key, out value) ? value : default(TValue); } set { base[key] = value; } } } 

Il convient de souligner que HybridDictionary le fait par défaut. Vous perdez le type générique, mais obtenez la fonctionnalité null-if-not-found.

Et (au moins théoriquement), je pense que vous obtenez des avantages de performance à un faible nombre de valeurs.

Je l’ai déjà fait auparavant et cela a très bien fonctionné pour simplement hériter de la classe Dictionary régulière et simplement masquer l’indexeur. C’est très propre de le faire de cette façon, de sorte que vous acquérez automatiquement toute la fiabilité et la familiarité de la classe Regular Dictionary.

 public class NullSafeDict : Dictionary where TValue : class { public new TValue this[TKey key] { get { if (!ContainsKey(key)) return null; else return base[key]; } set { if (!ContainsKey(key)) Add(key, value); else base[key] = value; } } }