views:

192

answers:

4

I'm implementing an AVL binary tree data structure in C# .NET 2.0 (possibly moving to 3.5). I've got it to the point where it is passing my initial unit tests without needing to implement any framework level interfaces.

But now, just looking through the FCL I see a whole bunch of interfaces, both non-generic and generic that I could implement to ensure my class plays nicely with language features and other data structures.

At the moment the only obvious choice (to me at least) is one of the Enumeration style interfaces to allow a caller to use the tree in a foreach loop and possibly with Linq later on. But which one (or more)?

Here are the interfaces I'm considering at present:

  • IEnumerable and IEnumerable<T>
  • IEnumerator and IEnumerator<T>
  • IComparable and IComparable<T>
  • IComparer and IComparer<T>
  • ICollection and ICollection<T>
  • IEquatable and IEquatable<T>
  • IEqualityComparer and IEqualityComparer<T>
  • ICloneable
  • IConvertible

Are there any published guidelines, either online or in book form, that provide recommendations regarding which framework interfaces to implement and when?

Obviously for some interfaces, if you don't want to provide that functionality, just don't implement the entire interface. But it appears there are certain conventions in the FCL classes (such as Collection classes) that perhaps we should also follow when building custom data structures.

Ideally the recommendations would provide guidance on such questions as when to use IComparer or IEqualityComparer, IEnumerable or IEnumerator? Or, if you implement a generic interface, should you also implement the non-geneic interface? etc..

Alternatively, if you have guidance to offer based on your own experiences, that would be equally useful.

+1  A: 

The definite guide is Framework Design Guidelines.
It gives Do, Do Not and consider recommendations. A lot of it is also available in MSDN.

weismat
Thanks, I'll take a look.
Ash
+1  A: 

I don't think you'll find a one-size-fits-all answer to this, as the implementation of these interfaces depends on how you expect your class to be used.

All these interfaces are provided to allow use of a class from within the existing BCL or the language itself. So, as you say, IEnumerable allows use of the class in a foreach loop.

Implementing each of these depends on the way that your library might be used. For instance, ICloneable is directly appropriate to users of remoting and sometimes users of ORMs. If your class makes sense in that environment then you'll help your users by implementing it.

Put another way: who's going to use your class? What BCL framework methods would you expect them to use?

(Also, many of the generic interfaces themselves implement non-generic versions, e.g. IEnumerable implements IEnumerable, and IEnumerator implements IEnumerator.)

Jeremy McGee
Yes, it's not a simple answer, but I'm interested in any guidelines for choosing between similar sounding interfaces, for example.
Ash
A: 

You just implement the ones that you want your class to have, i.e.

  • If you want to be able to enumerate items, then implement IEnumerable and IEnumerable<T> instead of inventing your own methods.
  • If you want to be able to deep-copy a tree, then implement ICloneable instead of inventing your own method.
  • If you want to be able to compare trees for value equality, then implement all the equality interfaces.

And so on. Other points:

  • Some interfaces should be implemented in a specific manner so as to avoid "surprising" behaviour in code that calls them. The equality interfaces in particular must be 100% consistent with each other and the System.Object virtual equality methods.
  • When there is a generic and non-generic version of the same interface, it is always useful to implement both.
Christian Hayter
Yes, I realise this. My question is what guidelines, conventions etc that help choosing between similar looking interfaces (IEquatable, IEqualityComparer) etc. I say this in my question.
Ash
I'd suggest, if you are going to implement equality, go for broke and implement them all.
Christian Hayter
+2  A: 

You should be aware that some of those interfaces inherit from each other (ICollection<T> and IEnumberable<T>) and the generic versions of interfaces generally require the implententation their non-generic versions. IEnumerator and IEnumerable are connected (an IEnumerable traditionally creates an IEnumerator to do the enumeration)

Implementing IComparable<T> is on a collection is fraught with danger (are you comparing the members in the collection?), and IComparer<T> is a helper interface for sorting methods.

ICloneable is a bit outdated - but it's meant to enable creating a deep copy (which, again, is fraught with danger for a collection.

I'd take issue with your

Obviously for some interfaces, if you don't want to provide that functionality, just don't implement them.

If you implement an interface, you should implement all the members of it. (see Liskov Substitution Principle)

Implementing IConvertible for a collection also seems strange - you might perfer to implement ISerializable.

The MSDN Documentation for the interfaces is a bit terse, but you can always google them to see how they work.

David Kemp
Thanks David, some useful tips. You misunderstood my point on not implementing interfaces. I mean the entire interface, not just certain methods. ie, if you don't want to provide cloneable functionality, don't implement ICLoneable at all. I've changed the wording in my question.
Ash
@Ash - sorry, completely misunderstood that point
David Kemp