Following on from a taster that I posted before: http://lordzoltan.blogspot.com/2010/09/pseudo-template-meta-programming-in-c.html, here goes with the next piece.
Firstly, the design I'm showing here can be applied to any reflection scenario - I've deployed it in various guises, including for storing meta-data about methods in a class, properties, and even for compiling dynamic methods that provide late-bound functions. The primary reason why this design, I feel, is particularly useful is that you're making the .Net type engine do all the hard work of storing all this meta-data for you.
I'll go through this by looking at a really simple scenario:
You want to write a system whereby you can reflect properties of a type that a developer writes in order to provide some form of functionality. Let us say that you want to write a dictionary state-bag wrapper, where a property is wrapped around an internal dictionary of values. What you want is to have a code-generation friendly way for developers to add such properties to their class with the minimum of fuss. So, retrieving a property becomes as simple as:
You don't necessarily want to have the names in the state bag to be the same as the names of the properties, so you need to be able to allow the developer to specify, with the minimum of fuss, what the backer for the property is actually called.
The perfect solution to this is an attribute:
Now we can use the attribute as follows (now making a class that we can use later):
So far so good, but we need a base class to be called. What we're going to do here is to produce two levels of abstraction: an interface and a base class that we can inherit from. The base class will implement the interface to provide a simple implementation of a dictionary. Other implementations could wrap around a data row, ASP.Net ViewState or SessionState and so on.
For simplicity's sake we'll say that an implementation of GetValue must return null if a value with the supplied name doesn't exist. And SetValue should quietly overwrite existing values, while adding new ones.
Our base class, then, looks like this:
And that's it, now we can write the outer class, with a couple of minor adjustments, for the wrapped property that I outlined first, so that it inherits from DictionaryPropertyClass.
But wait, what about this mapping business? Well, that's where we enter the realm of static generics.
For anyone still getting to grips with generic types, they are basically classes with type parameters - unlike C++ templates, however, which are built at compile time in a template-style, the generic class itself (without any type parameters) is a type in its own right - except it cannot be used to create an instance.
TIP: For anyone already doing run-time wizardry with generics - try typeof(MyGeneric<>) - it returns a reference to the generic type rather than an instance of the generic type. If your generic has multiple parameters, then you simply include a comma for each additional parameter. typeof(MyGeneric<,,>), for example, returns a reference to the System.Type of a generic that takes three type parameters.
What we're going to do is to create a static generic class (i.e. we never actually create an instance of it) which is going to act as a container for the reflected property information for any type:
Believe it or not, that's basically it. From outside of our class MyClass, we can now interrogate property mappings with the following line of code:
The first time a call like this is made, the .Net runtime will fire the static constructor for DictionaryClassMetaData<MyClass> (remember it's not a proper type until it's actually got a generic parameter in it!), after which it calls the GetKey method. After that initial call, however, it knows that it doesn't need to call it again, so it just fires the method. What's more, the .Net runtime ensures thead safety for that call, too, so you don't have to worry about locking anything in a multithreaded environment.
How does this help us with the initial plan? At the moment, we could reimplement our SomeProperty property in MyClass as follows (only the get accessor code is listed now):
Hmmm, smelly isn't it... What's more - the developer implementing their class needs to know about the meta class - which is no good.
This is where I admit that I've led us down a blind alley - the truth is that the original idea of using base.Foo(bar) as a methodology isn't going to cut it. Although, as you'll see in a minute, we could implement it that way, there is a better way that we can do it. Generic extension methods:
Now, we can rewrite our implementation of MyClass' SomeProperty as follows (this time, I'll include the full code):
Note how the implementation now uses 'this' directly. This is a strange feature of extension methods: when inside class scope, you will not see extension methods defined for that type unless you explicitly use 'this' - this is because without the 'this', the intellisense (and presumably the compiler) has no way of knowing that you actually mean 'this'. This is different from instance members - because they are within scope at the point where you are writing the code (when inside a class definition), therefore they are implicitly available.
We're also using the 'this' as the type parameter for our GetMappedValue extension method. If you were to breakpoint the extension method and do a get on the property, you will see that 'T' has been implicitly fed into it as MyClass. This is how we are then able to invoke the static meta class without any real effort whatsoever.
Now that you have a static meta class that you can invoke in a single line of code, you can also do it with reflection. Consider this line of extremely generic code, which could be thought of as being similar to how the databound controls in Windows Forms, WPF and ASP.Net get hold of values when using the 'DataTextField' and 'DataValueField' properties:
So now we're faced with a slight problem - we can't talk directly to the meta data class, because we don't have a T to work with. If only we could dynamically invoke the meta class... Hopefully, if you didn't already know it, you have probably guessed that we can. This time we're using dynamic generics - and this is where my little tip earlier on of using typeof(SomeGeneric<>) comes in really handy:
An interesting note about this code - even when you access a static class via reflection in this way, .Net will still maintain it's rule of firing the static constructor just once. Also, I'll wager that the speed with which it can obtain the type instance after the initial call is pretty good.
You can (and I have) cache the method for future use in a Delegate stored within the instance (think 'Delegate.MakeDelegate(typeof(Func<string, string>), reflectedmethod);') - this speeds up future access a great deal, because the JIT can optimize away a lot of the method-jumping.
There are some truly crazy things I've done with this model and I might blog about them in the future when I get a chance - in the meantime I hope this provides satisfaction for your coding needs!