Подтвердить что ты не робот

Как получить значение null вместо ключа KeyNotFoundException для доступа к значению словаря по ключу?

В каком-то определенном сценарии мне показалось полезным иметь короткий, читаемый способ получить null вместо KeyNotFoundException при доступе к значению словаря по ключу, когда в словаре нет такого ключа.

Первое, что мне пришло в голову, это метод расширения:

public static U GetValueByKeyOrNull<T, U>(this Dictionary<T, U> dict, T key)
        where U : class //it 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; 
}

Но это не очень коротко сказано на самом деле, когда вы пишете что-то вроде этого:

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

Я бы сказал, что было бы намного лучше, чем синтаксис базового синтаксиса: dict["key4"].

Затем я придумал идею иметь класс с полем словаря private, который раскрывает нужные мне функции:

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

Но, кажется, немного накладные расходы, чтобы получить небольшое изменение в основном поведении.

Еще одним обходным решением может быть определение Func в текущем контексте следующим образом:

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

поэтому его можно использовать как GetDictValueByKeyOrNull("key1").

Не могли бы вы дать мне больше предложений или помочь выбрать лучший?

4b9b3361

Ответ 1

В конце концов я придумал вариант, используя вывод из словарного класса с явной реализацией интерфейса:

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;
            dict.TryGet(key, out val);
            return val;
        }
    }
}

Таким образом, он предоставляет функциональные возможности, которые мне нужны следующим образом:

//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");

Ответ 2

Вот мое решение из моей личной библиотеки, реализованное как метод расширения. Я только отправляю его, потому что он реализован из интерфейса словаря и позволяет передавать необязательное значение по умолчанию.

Реализация

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

Использование

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

Ответ 3

Вы не можете получить синтаксис, который вы хотите, с помощью метода расширения, и, как утверждают другие, переопределение метода/оператора для изменения его поведения, как правило, не является отличной идеей. Я думаю, что лучшее, что вы можете сделать, это сократить имя, которое вы используете.

Это, если вам нужно поддерживать интерфейс IDictionary. Если вы не взаимодействуете с каким-либо кодом, который ожидает IDictionary, то вы можете свободно определять свой собственный интерфейс и работать с другим оператором [] не проблема.

Независимо от того, что вы вызываете функцию, вам нужно реализовать ее следующим образом:

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

Он просто выполняет поиск по сравнению с 2 для ваших реализаций.

Ответ 4

Добавить класс DictionaryExtension

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

И он может вернуть Значение по умолчанию, если не найден ключ в словаре.

Значение по умолчанию равно null, если это ссылочный тип.

_dic.GetValueOrDefault();

Ответ 5

Полагаю, что я бы использовал не. Ключевое слово new, хотя и полезно в этом случае, может создавать ошибки, которые очень трудно найти. Кроме этого, вы можете попробовать этот класс.

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

Ответ 6

Стоит отметить, что HybridDictionary делает это по умолчанию. Вы теряете типичный тип, но получаете нулевую, если не найденную функциональность.

И (по крайней мере теоретически) вы получаете выгоды от производительности при низком количестве значений, я думаю.

Ответ 7

Я делал это раньше, и он работал достаточно хорошо, чтобы просто наследовать обычный класс Dictionary и просто скрыть индекс. Это действительно так, чтобы сделать это таким образом, чтобы вы автоматически приобрели всю надежность и знакомство с классом обычного словаря.

public class NullSafeDict<TKey, TValue> : Dictionary<TKey, TValue> 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;
        }
    }
}