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.

2 comments :

Anonymous said...

Perhaps the friction you feel is an indication that you are testing and mocking at too low a level. Seriously, you test the collection's implementation and then you TRUST IT. You test the lower-level objects and then you TRUST THEM. And then you mock the much-higher level transactions and know that if the lower-level bits work (and you've got test code to prove it), only your new code can be to blame.

Steve Gilham said...

If it were all my code I wouldn't be starting from here...

It's having to substitute for a live AD instance when running unit tests.

Writing a DomainAccessor class and trusting that -- and also providing a mock for the rest of the code to use, fine.

But then it comes time to write the tests for the DomainAccessor, when I have to provide a shimming layer for code inside it that looks like


// Domain -> Forest --> GlobalCatalog gymnastics omitted
DirectorySearcher searcher = catalog.GetDirectorySearcher())
searcher.PropertiesToLoad.Add(...);
searcher.PropertiesToLoad.Add(...");
SearchResultCollection scResults = searcher.FindAll();

foreach (SearchResult scResult in scResults){
foreach (object value in scResult.Properties[...])
// Finally, get data

that I have to write something to model AD in all its gory details, and all the nested special-purpose collection classes (SearchResultCollection, SearchResultValueCollection.
PropertyCollection, PropertyValueCollection).

Using generics means only having to shim the underlying ICollection interface once -- which is a good thing (and I can re-use that for any other bits of .Net library code I have to do mocks for), hence the "almost useful".

The main complaint is about not being able to specify an arbitrary construction interface as a constraint e.g. T : new(U) -- whereas you can blithely assert this in C++ templates (or Ruby or Python code, where I'd get the additional benefit of only needing to parametrise on the return interface).