Are there any collections in .NET that prevent null entries?

Viewed 8530

I'm specifically thinking about a collection that fulfills the contract of a set, but I think the question can apply to any kind. Are there collections in the .NET framework that prevent null entries? The specific behavior I want is this:

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

This isn't the behavior of the built-in HashSet<T>. Are there any collections that do have this (or similar) behavior, or am I better off rolling my own? If the latter, what's the best way to go about it? Should I inherit directly from HashSet<T> or just wrap it?

EDIT: To be clear, this is just idle wondering. Mostly because I can't think of any reason I'd ever want to allow null into a set of objects. I don't have any particular need for this.

10 Answers

There isn't a built-in class just like HashSet<T> except for this single behavior.

If you need this, I'd recommend rolling your own. I do not recommend subclassing HashSet<T>, however. None of the methods (like Add, which you explicitly want to change) are virtual, as it wasn't really designed with subclassing in mind. This will cause strange usage behavior, since you'd be hiding the inherited methods.

Just encapsulate a HashSet<T>, and expose the members you need. The only real "code" you'd have to add is a single null check on the Add method - otherwise, just pass through all of the methods to the encapsulated instance.

If you want this to be a generic class, you'll need to add an extra constraint to only work with classes, since you want to have a null check:

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

What about writing an extension method for HashSet. It may be the easiest thing to do.

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);
    }
}

You can then do this:

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

As you tagged this question with generics I suppose you are looking for a generic collection. Such collection does not exist in the .NET framework simply because value types cannot be null, so I guess you should have to roll your own by adding a constraint restriction on the generic type it accepts.

A System.Collections.Generics.Dictionary<TKey, TValue> doesn't let you add null for TKey (throws an exception). You'd have to ignore TValues then in your scenario if you intend to use it that way, and the functionality would be similar to the Hashset<T>, except the fancy set operations of course.

Surely a bit clumsy but maybe it's a liable workaround for you in the meantime before you can come up with your own fancy collection type.

I am not aware of any .NET collection that does that, as based on the type passed a null is actually a valid entry, thus, the add is successful.

I would most likely start off from the System.Collections.ObjectModel.Collection class for rolling your own.

Two options:

Edit: Sorry I missed he needs a set and not a collection. At least the preferred item I listed is still valid. :)

Preferred:

In the method that adds items to the collection, throw an ArgumentNullException if they are called with an invalid value.

Not preferred:

Derive from Collection<T> and override InsertItem and SetItem to throw an ArgumentNullException if the value is null.

In any case, you could use Code Contracts in .NET 4.0 to prevent null values to be added into a collection. This way, you'll also get a compile-time checking whether the code is correct (using static analysis provided by 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);
  }

}

The method marked with ContractInvariantMethod specifies condition that should hold at all times (there are no null elements in the list). I believe that the static analysis cannot reason about this (because of ForAll), but I may be wrong. However, it certainly can check whether you're calling Add method with correct (non-null) arguments.

You're gonna have to roll your own. If you want Add to return a bool (whether the item was non-null and therefore added or the inverse), you'll have to start from scratch. I might recommend inheriting from List and throwing ArgumentNullExceptions. I mean if you really don't want nulls added, exceptions might be the best way and will certainly be easier.

Related