Как получить значение 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")
.
Не могли бы вы дать мне больше предложений или помочь выбрать лучший?
Ответы
Ответ 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;
}
}
}