Fog Creek Software
Discussion Board




Welcome! and rules

Joel on Software

Is this abstract base method implemented?

Here's something I don't understand about how the CollectionBase class is implemented:

I can declare:

    class Derived : CollectionBase
    {
    }

I can instantiate this Derived class:

    Derived derived = new Derived();

I cannot call an Add method as I haven't declared it:

    derived.Add(1); //compiler error: Derived does not contain a definition for Add

However CollectionBase derives from IList, IList declares Add as public abstract, and I can successfully call Add through the IList interface:

    IList ilist = derived;
    ilist.Add(1);

So what is happening here:

-    If neither CollectionBase nor Derived implement Add, then why can I instantiate Derived, and call Add through the IList interface?

-    Or if CollectionBase does implement Add, then why can't I see it in the Object browser and why do I get an error when I try to call it?

Christopher Wells
Thursday, April 08, 2004

CollectionBase requires you to implement your own Add() method that delegates to the inner list.  This way, the Add method you define will be strongly-typed for the correct type.  (For example, define the Add method for an EmployeeCollection to accept only Employee objects.)

You shouldn't use the IList interface to manipulate the contents of a strongly-typed collection, as it breaks the strongly-typed nature of the collection.  (For example, you could mistakenly add a Customer to the EmployeeCollection.)  Consider this just to be something of a bug.

If you want to ensure that others don't misuse the IList, you could implment OnInsert/OnRemove/OnSet/OnValidate in your derived collection.  That way, you can raise a run-time exception if someone tries to add an incorrect type using IList.  An example here:

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemcollectionscollectionbaseclassilistaddtopic.asp

Can't wait until we get generics with Whidbey and get rid of this CollectionBase nonsense.  <g>

Robert Jacobson
Thursday, April 08, 2004

I understand that that's how I'm supposed to use CollectionBase.

What I don't understand is how CollectionBase itself is is declared or implemented: how is it that a Derived : CollectionBase can derive from IList, and be instantiable, when the Add method isn't implemented in Derived nor in CollectionBase, but is defined as abstract in IList?

Christopher Wells
Thursday, April 08, 2004

I am pretty new to C# (2-3 weeks) so this might be totally wrong, but:

Could it be, that CollectionBase declares an abstract override Add method?

Daren Thomas
Thursday, April 08, 2004

You guys are really sure that you don't want to simply use CodeSmith instead of dicking around with CollectionBase...?

Chris Nahr
Thursday, April 08, 2004

"Or if CollectionBase does implement Add, then why can't I see it in the Object browser and why do I get an error when I try to call it?"

I wanted to isolate this question, because it's a good one.

When a class implements an interface, that doesn't necessarily mean that those interface methods ALSO become part of the signature of the class itself. The methods on the interface and the methods on the class are separate entities.

It just so happens that most of the time, you really do want those interface methods on the class, so that you don't have to downcast to the interface pointer to call them.

To illustrate the difference, consider:

public class CollectionBase : IList {
  public int Add(object o) {
  }
}

vs.

public class CollectionBase : IList {
  int IList.Add(object o) {
    [...]
  }
}

The former adds a method to the signature of CollectionBase that also happens to suffice as an implementation of IList.Add(), so it's used by default. The latter declares a specific IList.Add() implementation whose signature is not part of the object itself.

As someone alluded to earlier, the reason CollectionBase chooses the latter is because the purpose of CollectionBase is to be the base of a type-safe collection. You cannot change the signature of IList: it requires Add to take an object. However, in the signature of the derived class itself, the point of Add is supposed to be that it takes a typesafe parameter (and that type-safe Add method is unrelated to the IList interface, by the way).

So CollectionBase opts for explicit implementation, so that direct users of your type-safe collection class will have no choice but to pass in type-safe objects to Add() (since the other is hidden on the object itself and not accessible).

Users who use the IList interface will be able to add any type of object they want, so CollectionBase also invented an event-based validation pattern that allows you to intercept any accesses to the IList interface to ensure they are type-safe.

Brad Wilson (dotnetguy.techieswithcats.com)
Thursday, April 08, 2004

"You guys are really sure that you don't want to simply use CodeSmith instead of dicking around with CollectionBase...?"

I agree with this sentiment, too. Implementing type-safe collections is grunt work that's perfect for code generation. Nobody wants to spend their time creating new variations of CollectionBase and DictionaryBase. :-p

Brad Wilson (dotnetguy.techieswithcats.com)
Thursday, April 08, 2004

"Can't wait until we get generics with Whidbey and get rid of this CollectionBase nonsense."

Unfortunately, that's all they'll be good for, since they aren't latent typed generics. :(

Brad Wilson (dotnetguy.techieswithcats.com)
Thursday, April 08, 2004

Those type casts doesn't save the day after all, so it would be simpler to just make them optional than introduce generics.

The compiler knows when two types are incompatible anyway, so type casts between those is not necessary. Upcasts doesn't have to be explicit, that leaves downcasts. Downcasts are either incompatible or possible compatible. The compiler knows when they are incompatible. Nobody knows the outcome of the possible compatible situation until runtime.

The single most used downcast must be downcasting from object, but that case is always a possible compatible, which means that nobody knows at compile time if any downcast is valid or not, except when the type cast is incompatible with the variable type.

So when did you need those explicit type casts again? What's the point to enforce something which doesn't add anything? The cries for generics proves that something is wrong.

Here's an example to prove how meaningless explicit type casts can become:

class MyBase {}
class MyDerived : MyBase {}

MyDerived d = (MyDerived)new MyBase();

The compiler gives the proper error without the typecast, but happily accepts the code with it. It won't survive at runtime, of course.

Thomas Eyde
Thursday, April 08, 2004

If you want to see the specific implementation, look at the Rotor source code for CollectionBase:

http://www.123aspx.com/rotor/rotorsrc.aspx?rot=40278

Disclaimer: the Rotor code isn't guaranteed to be the actual code that ships with the .Net Framework, but it's probably close.

Robert Jacobson
Thursday, April 08, 2004

*  Recent Topics

*  Fog Creek Home