views:

183

answers:

8

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.

+1  A: 

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.

Mitchel Sellers
Collection wouldn't be appropriate to implement a HashSet - it doesn't have the same storage semantics, and performance characteristics.
Reed Copsey
That is a good point.
Mitchel Sellers
+2  A: 

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.

Darin Dimitrov
Why can't you just restrict it to nullable objects with `where T : class`?
Sean Devlin
Sure, in case you write your own collection.
Darin Dimitrov
@Sean Devlin: That basically is my answer... ;)
Reed Copsey
+7  A: 

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...
Reed Copsey
Make sure to implement `ICollection` if you implement `ICollection<T>`. The latter does *not* implement the first, and it has made some code rather annoying where I want to allow usage of both `HashTable` and `HashSet<T>`...
280Z28
@280Z28: Good point. I always thought it strange that ICollection<T> doesn't implement ICollection. Thanks for adding that.
Reed Copsey
This seems like the best advice for someone looking to go this route. Accepted.
Sean Devlin
+1  A: 

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.

herzmeister der welten
It's pretty expensive as well (allocates unused memory).
280Z28
Since this throws an exception that has to be caught, I'd say it isn't a good idea..
Mitchel Sellers
I wouldn't use this myself whatsoever, just was a proposal :-> ... Depends on the scenario after all if an exception is or is not sensible.
herzmeister der welten
A: 

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.

statichippo
I don't see why you'd have to start from scratch. `HashSet<T>.Add` already returns `bool`. It seems like could just return `false` in case of `null` and otherwise defer to `HashSet<T>`. EDIT: Since the method is not virtual, you would need to defer to `HashSet<T>` explicitly for everything, which would be a pain. But still better than starting from scratch.
Sean Devlin
-1: The ***only*** reason you can *ever* derive from `List<T>` is to provide a constructor that takes a special kind of initializer or to provide new operations. You absolutely cannot override its behavior on Add. See my answer here: http://stackoverflow.com/questions/1192022/how-to-create-a-custom-collection-in-net-2-0
280Z28
A: 

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.

280Z28
+2  A: 

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;
AGoodDisplayName
I almost always choose a solution to avoid extension methods, so I wouldn't use this. That said, I made some updates so it's usable should someone go that way. :)
280Z28
A: 

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.

Tomas Petricek