C # - нужна реализация IDictionary, которая позволит нулевой ключ - PullRequest
13 голосов
/ 16 декабря 2009

В принципе, я хочу что-то вроде этого:

Dictionary<object, string> dict = new Dictionary<object, string>();
dict.Add(null, "Nothing");
dict.Add(1, "One");

Есть ли какие-либо встроенные в библиотеку базовых классов, которые позволяют это? При добавлении нулевого ключа предыдущий код выдаст исключение во время выполнения.

Спасибо

Ответы [ 5 ]

11 голосов
/ 16 декабря 2009

Вы можете избежать использования 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");

Этот подход не будет работать, если вы намерены сделать вашу коллекцию более строго типизированной - например, вы хотите, чтобы ключ был строкой. Поскольку строка запечатана, вы не можете наследовать ее, чтобы создать «специальное значение», заменяющее ноль. Ваши альтернативы становятся немного сложнее. Вы могли бы:

  1. Создать какое-то специальное постоянное значение для представления «пустого» / «нулевого» случая. Хакерский и определенно путь к путанице. Это может быть жизнеспособным подходом, если словарь является полностью приватным для некоторого класса реализации, и вы можете написать некоторые служебные методы Encode / Decode, чтобы избежать распространения знаний о том, как вы переводите ключи повсюду.
  2. Создайте собственную реализацию IDictionary, которая внутренне делегирует экземпляру Dictionary <> - за исключением случая нулевого значения. Это нарушает задокументированные ожидания для интерфейса IDictionary <>, который говорит, что нулевые ключи должны вызывать исключение. Но вы можете сойти с рук, если это единственный способ решить вашу реальную проблему. Это работает, только если вы владеете и создаете экземпляр словаря.
  3. Найдите способ решить вашу проблему, не сохраняя «нулевой» ключ в словаре. Например, рассмотрите возможность не заполнять нулевой ключ в словаре и иметь некоторую специальную логику для решения этой проблемы. Ключи должны быть хэшируемыми и сопоставимыми для работы с базовой реализацией, поэтому null обычно запрещен.

Кроме того, нужен ли ваш словарный ключ для ключа object? Это может привести к незначительным ошибкам из-за использования ссылочного равенства, если вы предполагали, что Equals () будет оцениваться в качестве основы для сравнения.

5 голосов
/ 16 декабря 2009

Как насчет этого?

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; }
        }
    }
}
2 голосов
/ 16 декабря 2009

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
    }

}
0 голосов
/ 08 марта 2014

Нет необходимости в другой реализации Dicionary.

Посмотрите на мой ответ здесь: https://stackoverflow.com/a/22261282/212272

Вы также сможете сохранять типизированный словарь:

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]);
0 голосов
/ 16 декабря 2009

Ключ должен быть буквально 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);
...