119 votes

Comment obtenir null au lieu de l'exception KeyNotFoundException en accédant à la valeur du dictionnaire par la clé ?

Dans un certain scénario, il m'est apparu utile d'avoir un moyen court et lisible d'avoir null au lieu de la KeyNotFoundException lors de l'accès à la valeur du dictionnaire par la clé, lorsqu'une telle clé n'existe pas dans le dictionnaire.

La première chose qui m'est venue à l'esprit est une méthode d'extension :

public static U GetValueByKeyOrNull<T, U>(this Dictionary<T, U> 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 on écrit quelque chose comme ça :

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

Je dirais qu'il serait bien mieux d'avoir quelque chose de proche de la syntaxe de base : dict["key4"] .

Alors j'ai eu l'idée d'avoir une classe avec un private qui expose la fonctionnalité dont j'ai besoin :

public class MyDictionary<T, U> //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<T, U> dict;

    public MyDictionary()
    {
        dict = new Dictionary<T, U>();
    }

    public U this[T key]
    {
        get
        {
            if (dict.ContainsKey(key))
                return dict[key];
            else
                return null;
        }
        set
        {
            dict[key] = value;
        }
    }
}

Mais cela semble un peu excessif pour obtenir un léger changement dans le comportement de base.

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

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

afin qu'il puisse être utilisé comme GetDictValueByKeyOrNull("key1") .

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

101voto

Jared G Points 100

Voici ma solution issue de ma bibliothèque personnelle, implémentée comme une méthode d'extension. Je la poste uniquement parce qu'elle est implémentée à partir du dictionnaire. interface et permet de passer une valeur par défaut facultative.

Mise en œuvre

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

Utilisation

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

41voto

bmm6o Points 2692

Vous ne pouvez pas obtenir la syntaxe que vous voulez avec une méthode d'extension, et comme d'autres l'ont dit, surcharger une méthode/opérateur pour changer 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 le cas si vous devez respecter l'interface IDictionary. Si vous n'êtes pas en interface avec un code qui attend un IDictionary, alors 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.

Quel que soit le nom que vous donnez à la fonction, vous devez l'implémenter comme ceci :

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

Il ne fait qu'une seule recherche, contre deux pour vos implémentations.

27voto

horgh Points 8992

Finalement, j'ai trouvé une variante utilisant une classe dérivée du dictionnaire avec une implémentation explicite de l'interface :

public interface INullValueDictionary<T, U>
    where U : class
{
    U this[T key] { get; }
}

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

Il expose donc la fonctionnalité dont j'ai besoin de la manière suivante :

//create some dictionary
NullValueDictionary<int, string> dict = new NullValueDictionary<int, string>
{
    {1,"one"}
};
//have a reference to the interface
INullValueDictionary<int, string> 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");

25voto

TimChang Points 738

Ajouter un DictionaryExtension classe

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

Et il peut revenir Valeur par défaut si la clé n'est pas trouvée dans le dictionnaire.

La valeur par défaut est null s'il s'agit d'un type de référence .

_dic.GetValueOrDefault();

10voto

Brondahl Points 75

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

Et (au moins théoriquement) vous obtenez des avantages en termes de performances pour un faible nombre de valeurs, je pense.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X