Есть ли в .NET коллекции, которые запрещают нулевые записи? - PullRequest
15 голосов
/ 02 февраля 2010

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

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

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

РЕДАКТИРОВАТЬ: Чтобы быть ясно, это просто чудеса. Главным образом потому, что я не могу придумать ни одной причины, по которой я бы захотел разрешить null в набор объектов. У меня нет особой необходимости в этом.

Ответы [ 10 ]

12 голосов
/ 02 февраля 2010

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

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

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

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

public class ValueSet<T> : ICollection<T>, IEnumerable<T>, ICollection
    where T : class
{
     private HashSet<T> hashSet = new HashSet<T>();
     // ... Implement all members as needed...
7 голосов
/ 02 февраля 2010

Как насчет написания метода расширения для 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 голосов
/ 02 февраля 2010

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

1 голос
/ 06 июля 2016

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

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

1 голос
/ 02 февраля 2010

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

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

1 голос
/ 02 февраля 2010

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

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

0 голосов
/ 18 февраля 2017

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

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

    foreach (var item in set)
    {
        // safe to assume item is not null
        item.DoSomething();
    }
}
0 голосов
/ 02 февраля 2010

В любом случае вы можете использовать кодовые контракты в .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 с правильными (ненулевыми) аргументами.

0 голосов
/ 02 февраля 2010

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

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

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

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

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

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

0 голосов
/ 02 февраля 2010

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

...