Существуют ли какие-либо коллекции в .NET, которые предотвращают нулевые записи?

Я специально думаю о коллекции, которая соответствует контракту набора, но я думаю, что этот вопрос может быть применим к любому типу. Существуют ли коллекции в .NET Framework, которые предотвращают появление нулевых записей? Мне нужно конкретное поведение:

var set = new HashSet<object>();
bool added = set.Add(null);
Console.WriteLine(added); // prints "False"

Это не поведение встроенного HashSet<T>. Существуют ли какие-либо коллекции, которые имеют это (или подобное) поведение, или мне лучше сворачивать мои собственные? Если последний, какой лучший способ это сделать? Должен ли я наследовать непосредственно из HashSet<T> или просто обернуть его?

EDIT: Чтобы быть ясным, это просто праздное удивление. В основном потому, что я не могу думать о какой-либо причине, которую я когда-либо хотел бы разрешить null в наборе объектов. У меня нет никакой особой необходимости.

Ответы

Ответ 1

Не существует встроенного класса, подобного HashSet<T>, за исключением этого единственного поведения.

Если вам это нужно, я бы рекомендовал перепродать. Однако я не рекомендую подклассы HashSet<T>. Ни один из методов (например, Add, который вы явно хотите изменить) является виртуальным, поскольку он не был специально разработан с учетом подкласса. Это вызовет странное поведение использования, поскольку вы будете скрывать унаследованные методы.

Просто инкапсулируйте a HashSet<T> и выставляйте членов, которые вам нужны. Единственный реальный "код", который вам нужно добавить, - это одиночная проверка нуля в методе "Добавить" - иначе просто передайте все методы инкапсулированному экземпляру.

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

public class ValueSet<T> : ICollection<T>, IEnumerable<T>, ICollection
    where T : class
{
     private HashSet<T> hashSet = new HashSet<T>();
     // ... Implement all members as needed...

Ответ 2

Как написать метод расширения для HashSet. Это может быть проще всего сделать.

public static class HashSetExtensions
{
    public static bool AddNonNull<T>(this HashSet<T> set, T item)
        where T : class
    {
        if (item == null)
            return false;

        return set.Add(item);
    }
}

Затем вы можете сделать следующее:

HashSet<object> test = new HashSet<object>();
test.AddNonNull(null); //Will return false;

Ответ 3

Как вы отметили этот вопрос с помощью generics, я предполагаю, что вы ищете общую коллекцию. Такая коллекция не существует в платформе .NET просто потому, что типы значений не могут быть null, поэтому, я думаю, вам придется сворачивать свой собственный, добавив ограничение ограничения на общий тип, который он принимает.

Ответ 4

Мне не известно о какой-либо сборке .NET, которая делает это, поскольку на основе типа, переданного null, фактически является допустимой записью, поэтому добавление успешно.

Скорее всего, я начну с класса System.Collections.ObjectModel.Collection для того, чтобы вы могли переместить свой собственный.

Ответ 5

A System.Collections.Generics.Dictionary<TKey, TValue> не позволяет добавлять null для TKey (генерирует исключение). Вы должны проигнорировать TValue, а затем в своем сценарии, если вы намереваетесь использовать его таким образом, и функциональность будет похожа на Hashset<T>, за исключением операций с фантастическим набором, конечно.

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

Ответ 6

Даже если вам удастся исключить ДОБАВИТЬ нулевые записи, вы не сможете предотвратить ВХОД пустых записей внутри набора - по крайней мере, не с помощью вашего класса Set.

Возьмем любой Set<T>, где T - тип с нулевым значением. После того, как вы добавили некоторые ненулевые записи, вы можете взять любой из них и установить его в null. Поскольку эта команда не имеет ничего общего с классом Set, она не может быть обнаружена и не предотвращена классом Set.

Ответ 7

Два варианта:

Изменить: Извините, я пропустил, ему нужен набор, а не коллекция. По крайней мере, предпочтительный пункт, указанный мной, по-прежнему действителен.:)

Предпочтительно:

В методе, который добавляет элементы в коллекцию, введите ArgumentNullException, если они вызываются с недопустимым значением.

Не рекомендуется:

Вывести из Collection<T> и переопределить InsertItem и SetItem, чтобы выбросить ArgumentNullException, если значение равно null.

Ответ 8

В любом случае вы можете использовать Code Contracts в .NET 4.0, чтобы предотвратить добавление значений null в коллекцию. Таким образом, вы также получите время компиляции, проверяя правильность кода (используя статический анализ, предоставляемый Code Contracts):

public class MyClass {
  private List<Something> nonNullList;

  [ContractInvariantMethod]
  void NonNullEntries() {
    Contract.Invariant(Contract.ForAll(nonNullList, el => el != null));
  }

  public void Add(Something el) {
    Contract.Requires(el != null);
  }

}

Метод, отмеченный ContractInvariantMethod, указывает условие, которое должно сохраняться всегда (в списке нет элементов null). Я считаю, что статический анализ не может объяснить это (из-за ForAll), но я могу ошибаться. Однако, конечно, можно проверить, вы вызываете метод Add с правильными (не нулевыми) аргументами.

Ответ 9

Как ответили другие, нет класса Set, который не допускает null. Сказав это, я поделюсь простым решением, которое я использовал в нескольких ситуациях. В моем случае я разрабатывал методы, которые принимали аргументы HashSet, потому что мне нужно было обеспечить, чтобы я получал уникальные объекты. Я не хотел иметь возможный нуль в наборе, и я не хотел проверять значение null каждый раз, когда я перечислял элементы и выполнял над ними операции. Мое решение состояло в том, чтобы просто вызвать remove null в случае, если был null.

public void MyMethod(HashSet<SomeType> set)
{
    // remove null item in case it there
    set.Remove(null);

    foreach (var item in set)
    {
        // safe to assume item is not null
        item.DoSomething();
    }
}

Ответ 10

Тебе придется сворачивать. Если вы хотите добавить для возврата bool (независимо от того, был ли элемент ненулевым и, следовательно, добавлен или обратный), вам придется начинать с нуля. Я мог бы рекомендовать унаследовать от List и метать ArgumentNullExceptions. Я имею в виду, что если вы действительно не хотите добавлять нули, исключения могут быть лучшим способом и, безусловно, будет проще.