Showing posts with label generics. Show all posts
Showing posts with label generics. Show all posts

Tuesday, 20 April 2010

Using dynamic to simplify reflection (and: generic specialization)

As I’ve noted in the past; reflection and generics don’t mix very well. The story isn’t quite as bad in .NET 4 though; here’s an example of something that is just painful to call (for the correct T known only at runtime) in C# 3.0:

image

And remember – we’re talking about the scenario where T is only known at runtime:

image

Previously we needed to use lots of MakeGenericMethod (or MakeGenericType) – things like:

image

You can see how that might quickly become boring. But in .NET 4 / C# 4.0 we can exploit “dynamic” to do all the heavy lifting and cache a delegate per scenario for us:

image

Much better!

Another possible use of this is generic type specialization:

image

Now this version is called for `int` arguments, otherwise the generic version is called – just as would have been the case from static C#. Magic!

Tuesday, 12 May 2009

Generics, and When Not To

Generics are undoubtedly of huge benefit for writing maintainable, static typed code with the minimum of fuss... but can it be overused? I firmly believe that generics are great for regular line-of-business code, with typed collections/lists and everything else that goes with them. But what about utility code, in particular when you're doing something complex with reflection... maybe give it a miss.

A cautionary tale: if you find yourself doing something with generics that looks complex... stop; you might be using the wrong approach. Of course, you might be right after all - but at least do a sanity check.

Consider protobuf-net; this is a member serializer (akin to XmlSerializer, DataContractSerializer, et al - but writing binary). It is quite a complex setup, but essentially it is using a strategy pattern (via reflection) to serialize each of the different properties. To avoid lots of boxing/casting, and to allow me to use the typed form of delegate invoke (much faster than dynamic invoke), it started out with a highly generic model - i.e. strategy classes that use generics to describe the input, output and anything else required.

But the problem with generics in this context is that they beget more generics... you start with something simple like "Foo<T>", and before you know it you've got types with multiple generic type arguments and constraints, inheriting from other generic types, all the way down (just like the turtles). Understanding the type signature alone is headache inducing - never mind trying to use reflection (MakeGenericType etc).

More importantly, however - the generic approach doesn't work very well if you want to use a decorator-based strategy; for example, to serialize the property "public List<Foo> {get;}", we might have a decorator that does property access, a decorator that handles list iteration, and a decorator that serializes Foo (and whatever that involves). You simply can't write that just using generics, as you can't tell in advance how deep the hole goes, and how many type arguments you'll need (let alone what they might be).

For utility code (such as a serialization engine) there are some other considerations:

  • Some frameworks (Compact Framework) have limitations in the number of generic types you can create at runtime
  • Some frameworks (Micro Framework and .NET 1.1) simply don't have generics at all

So faced with ever-increasing code complexity (always a bad sign), and it not working reliably (or at all) on some frameworks, I've decided to try to drop generics completely from the core implementation, and just have an "object" based decorator-chain. It then doesn't matter how complex the chain gets - I know I can still pass an object downstream (even if I've changed the type), and it won't offend CF/MF.

So what about performance? I certainly don't want to cripple the code...

  • Passing objects between the different decorators in a chain might look ungainly, but actually the odd bit of boxing and casting isn't necessarily a major problem (especially if it means the code works)
  • Where available (i.e. framework dependent), we can still use Delegate.CreateDelegate and the generic wrapper trick, but now as an implementation detail of an otherwise non-generic decorator (i.e. the public API would still be non-generic)
  • Where available (i.e. framework dependent), we have the option of making our decorators support Reflection.Emit - so rather than having methods that do the operation, we can have methods that write how to do it. There are several performance advantages to this:
    • no boxing/unboxing/casting
    • no stack hops
    • no delegate invoke (dynamic or typed)

The refactor is still incomplete, but is slowly taking shape (although it is taking longer than I hoped). It helps that I have a good set of unit tests so that I know just how far I have to go... but I've done enough to satisfy me that a: it should work much more reliably on CF, b: it should still fly on the regular framework, and c: if I really want I could probably make it work on .NET 1.1 and MF (but I'm not promising to do this).

Tuesday, 17 March 2009

Compact Framework Woes, revisted

A cautionary tale for CF development...

Previously, I mentioned a compact framework permissions gotcha that was hurting protobuf-net. Other than making thinks public, I never got much further, but at least I can understand the issue.

Well, now I have hit another issue (in the wild); for a complex model, the VM is throwing a MissingMethodException - but simply when invoking a regular generic method - no reflection etc:

    prop = CreateProperty<T>(type, ref format);

This surfaces in a test case where we add lots of different (but very similar) types to the model - eventually it simply gives up, and can't resolve the generic method (even though it worked fine for the identical property on the last "n" similar types). One can only assume that it has hit an internal limitation of the CF runtime.

The good news is that the regular framework doesn't exhibit this; the bad news is that this doesn't help me... to support complex models I expect I will have to take out some of the generics, and use a bit more casting / boxing. Which probably won't kill me.

In truth, I've probably out-clevered myself through over-use of generics - and in some ways, it has stopped me using a decorator quite as cleanly as I'd like. Lesson learnt...