Deja Vu
If there's one thing that defines a modern language as much as its syntax, it's the way it handles collections. Therefore, it shouldn't be such a big surprise that most of my encounters with .NET quirks stem from the differences between its collection classes and those I've used in Java. Still, I hope this is won't turn out to be a tradition, otherwise I will have to rename this column to "Collection Corner".
This particular tale, just like the last one, starts with the Dictionary generic class. Specifically, it starts with my need to be informed when someone manipulates its contents. The classes I was writing had a rather simple purpose: associate event handlers with different input controller events. One class managed keyboard events, another mouse events and yet another gamepad events. Each one of these classes exposed at least one read-only property of appropriate Dictionary type. All went smoothly until I decided that I needed to know when someone added, removed or changed the handler for an event.
Keep Me Posted
The first logical thing to do was to check the documentation and see whether the Dictionary class itself offered the desired functionality. Despite my years of experience with Java collection framework, I hoped Microsoft would surprise me pleasantly. Good thing that I didn't bet on it, though. After all these years of evolution, the two most sophisticated mainstream languages still don't offer the fundamental courtesy provided by Delphi's good old TStringList class.
Just like I expected, I decided I would have to implement the desired behavior myself. The logical choice seemed to be to override whatever manipulates the collections contents: the Add, Remove and Clear methods, and the indexer. If you have any .NET experience, you're probably chuckling at my naïveté by now; if you don't, then I'll keep you in suspense a bit longer.
Virtually Polymorphic
Perhaps the greatest difference between Java and .NET, certainly the one I find most difficult to get used to, is the way they handle inheritance polymorphism. In Java, all non-private methods are virtual by default. If you don't want the derived classes to override a method, you have to explicitly declare it as final. Once a method is declared as final, your derived classes cannot contain a method with the same name.
In .NET, all methods are non-virtual by default. If you want a method to be overridable, you have to explicitly declare it as virtual. If you're overriding a method, you have to include the override keyword. If you want to keep a virtual method from being overridden, you have to declare it as sealed. And if you want to introduce a new method with the same name in the derived class, you have to use the new keyword.
At first, this seems like a lot of fuss to achieve virtually the same results, pardon the pun. However, these differences are extremely important, as they represent different design philosophies. The .NET way makes you think twice before you decide which parts of your code will be flexible and which set in stone. As Eric Lippert will tell you, this mitigates the notorious Fragile Base Class problem, which should be cool, right?
No Jack For Plug
The aforementioned philosophical differences are not all about how many polymorphic angels can dance on the head of a non-private pin. There are some very practical consequences of making all your methods non-virtual by default. One of these consequences is rather unfortunate: none of the methods in the Dictionary class are virtual. In other words, there's no way to modify the behavior of the Dictionary class so that you get a notification if its contents change. Or is there?
Everyone is really excited about the shiny new features in C# 3, such as partial methods. It will be interesting to see whether the collection classes will benefit from this change. I haven't downloaded Orcas yet, so I don't know. I won't hold my breath, though. Besides, a new version of the language and the surrounding framework won't help those who need to extend the collection classes now. What solution could they use?
Interface/Off
Fortunately, there is a way to hack around both the language limitations and the rigid design of the collection classes, without too much work. The key is to fall back to the interface polymorphism. Where before I exposed my dictionary as a read-only property of generic Dictionary type, now I have to change its type to generic IDictionary interface.
The second step is to include the IDictionary interface in the list of base types for my dictionary class, like this:
class MyDictionary<TKey, TValue> : Dictionary<TKey, TValue>, IDictionary<TKey, TValue> { // ... }
How does this help? It makes sure that the compiler will remap the interface methods to the corresponding methods in the class. If there is a method in MyDictionary whose signature corresponds to an IDictionary method, it will be mapped to it; if not, an inherited method will be used.
The final step is to declare the methods you want to modify as new methods:
public new void Add(TKey key, TValue value) { // ... base.Add(key, value); // ... }
That's all there is to it, really. Some things haven't changed that much since the bad old times of COM and COM+.
1 comment:
Thanks Voja! Nice tip, can help you avoid writing a lot of unnecessary delegation code.
Post a Comment