C# - нужна реализация IDictionary, которая позволит нулевой ключ
В принципе, я хочу что-то вроде этого:
Dictionary<object, string> dict = new Dictionary<object, string>();
dict.Add(null, "Nothing");
dict.Add(1, "One");
Есть ли встроенные в библиотеку базовых классов, которые позволяют это? Предыдущий код будет генерировать исключение во время выполнения при добавлении нулевого ключа.
Спасибо
8 ответов
Вы можете избежать использования null и создать специальный класс одноэлементных значений, который делает то же самое. Например:
public sealed class Nothing
{
public static readonly Nothing Value = new Nothing();
private Nothing() {}
}
Dictionary<object, string> dict = new Dictionary<object, string>();
dict.add(Nothing.Value, "Nothing");
dict.add(1, "One");
Этот подход не сработает, если вы намерены сделать свою коллекцию более строго типизированной - например, вы хотите, чтобы ключ был строкой. Поскольку строка запечатана, вы не можете наследовать ее, чтобы создать "специальное значение", заменяющее ноль. Ваши альтернативы становятся немного сложнее. Вы могли бы:
- Создайте специальное постоянное значение для представления "пустого" / "нулевого" случая. Хакерский и определенно путь к путанице. Это может быть жизнеспособным подходом, если словарь является полностью приватным для некоторого класса реализации, и вы можете написать некоторые служебные методы Encode/Decode, чтобы избежать распространения знаний о том, как вы переводите ключи повсеместно.
- Создайте собственную реализацию IDictionary, которая внутренне делегирует экземпляру Dictionary<> - за исключением случая null. Это нарушает документированные ожидания для интерфейса IDictionary<>, который говорит, что нулевые ключи должны вызывать исключение. Но вы можете сойти с рук, если это единственный способ решить вашу реальную проблему. Это работает, только если вы владеете и создаете экземпляр словаря.
- Найдите способ решить вашу проблему, не сохраняя нулевой ключ в словаре. Например, рассмотрите возможность не заполнять нулевой ключ в словаре и иметь некоторую специальную логику для решения этой проблемы. Ключи должны быть хэшируемыми и сопоставимыми для работы с базовой реализацией, поэтому null обычно запрещен.
Кроме того, нужен ли ваш словарный ключ ключ, чтобы object
? Это может привести к незначительным ошибкам из-за использования ссылочного равенства, когда вы, возможно, предполагали, что Equals() будет оцениваться как основа для сравнения.
Как насчет этого?
public class NullableDictionnary<T1, T2> : Dictionary<T1, T2>
{
T2 null_value;
public T2 this[T1 key]
{
get
{
if (key == null)
{ return null_value; }
return base[key];
}
set
{
if (key == null)
{ null_value = value; }
else
{ base[key] = value; }
}
}
}
NameValueCollection может принимать пустой ключ, но он не реализует IDictionary. Однако было бы довольно легко извлечь из DictionaryBase и предоставить Add/Remove/Indexers и т. Д., Которые просто заменяют null чем-то встроенным, например:
class MyDictionary : DictionaryBase {
private readonly object nullKey = new object();
void Add(object key, string value) {
if ( key == null ) { key = nullKey; }
.. call base methods
}
}
Если ключ является перечислением, вы можете использовать несуществующее значение вместо нуля, например(YourEnum)(-1)
Вы можете просто использовать ValueTuple как оболочку для ключа, например:
Dictionary<ValueTuple<string?>, string>
Нет необходимости в другой реализации Dicionary.
Посмотрите на мой ответ здесь: /questions/9965476/slovar-s-nulevyim-klyuchom/9965488#9965488
Вы также сможете сохранить типизированный словарь:
var dict = new Dictionary<NullObject<int?>, string>();
dict[1] = "one int";
dict[null] = "null int";
Assert.AreEqual("one int", dict[1]);
Assert.AreEqual("null int", dict[null]);
Ключ должен быть буквально NULL? Ключ в коллекции является индексом. Для меня не имеет большого смысла иметь NULL для индекса в коллекции.
Может быть, создать новый класс
public class ObjectEntry
{
public object objRef;
public string desc;
public ObjectEntry(object objectReference)
{
objRef = objectReference;
if (objRef = null) {desc = "Nothing";}
else {desc = objRef.Description;} //or whatever info you can get from a proper objRef value
}
}
newObj = new ObjectEntry(null);
dict.add(newObj, newObj.desc);
Небольшое изменение ответа Джестро, чтобы сделать более чистое (для меня) решение, которое делает более ясным, что вы пытаетесь сделать. Очевидно, это может быть расширено по мере необходимости. Но вы понимаете, просто сделайте обертку.
public class NullDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
private TValue _default;
public new TValue this[TKey key]
{
get {
if(key == null)
{
return _default;
}
return _decorated[key];
}
}
private Dictionary<TKey, TValue> _decorated;
public NullDictionary( Dictionary<TKey,TValue> decorate, TValue defaultValue = default)
{
_decorated = decorate;
_default = defaultValue;
}
}