Saturday, June 14, 2008

C# generics — almost useful

This week I have spent an inordinate amount of time doing more C# generics coding than I have before in my life. And there have been very few things in the recent past that has made me feel that I'd rather be coding proper C++, if I couldn't be using Python or Ruby (which would make the task almost trivial).

The problem -- make a set of interfaces to wrap part of the System.DirectoryServices and System.DirectoryServices.ActiveDirectory namespaces, so as to make them mockable.

What seemed an easy job of mocking about half a dozen classes and a few methods on each has turned into a major exercise, because they all return different trivial collection classes (like this one) which don't present any public constructors.

Writing a basic Wrapper<T> to hold a T provided at construction time, subclassing it to do delegation for standard interfaces like IDisposable -- or even ICollection -- no problem, defining interfaces for the parts of the concrete types and writing specific Wrapper subclasses, easy.

The problem comes when wrapping a collection of type T, which contains entities of type U, which have to be exposed as wrapper sub-type V (subclassing Wrapper<U>). Not only do I have to suppress FxCop nagging about 3-way generics, but when I write the this[string index] or the Current property of a custom Wrapper<IEnumerator>, I would like to write

return new V(Wrapped[index]);
or in expanded form with the types made explicit
U internal = Wrapped[index]; //delegate to wrapped object
return new V(internal);

but I can't, because I'm only allowed to get at a public default constructor for V -- unlike the duck-typing in a C++ template, which would allow me to write the desired code. Which means I'm forced to violate the desired immutability and put a setter on the Wrapped property, and allow null internal objects. At least I can put a generic wrapping function V Wrap<U,V>(U input) in the base class which can be the only thing to use it (by making the setter private) -- and then suppress the FxCop nagging on the generic function about having generic types not implied by context.

And talking of FxCop nagging -- on more than one occasion a concrete wrapper named for the wrapped type Win32[NameOfWrappedType], or method in the partial interface, got hauled up on violating naming conventions because the MSFT prototype name did so. *sigh* My code gets more and more impure, all the while as I'm simply writing code and unit tests for that code, which I wouldn't need to have written at all if it weren't for unit testing the code I really want to write.

Post a Comment