snr
Runtime handler registration in .NET
I've been working on some home automation software in C# for a friend and hit a bit of a stumbling block the other day. I needed to be able to register some message handlers for this application at runtime.
To do this, one needs to use the reflection framework - this allows you to inspect the assembly types and metadata at runtime, and is useful for this type of thing. This isn't exactly rocket science, and one can quickly throw together a quick example to discover each of the types that implement a given Interface/Subclass and make a method call to a static method on the class:
// Find all the types in this AppDomain that implement the IMessage interface
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (Type type in assembly.GetTypes())
{
if( type.IsSubclassOf( typeof(MessageBase) ) )
{
MethodInfo method = type.GetMethod("ImplementsMessage");
MessageType msgType = (MessageType)method.Invoke(null, null);
_messageHandlers.Add(msgType, type);
Query the type and see what protocol message it handles
try
{
// If any bit of this fails, we'll fail silently which is OK
ConstructorInfo constructor = type.GetConstructor(new Type[0]);
IMessage obj = (IMessage)constructor.Invoke(new object[0]);
_messageHandlers.Add(obj.ImplementsMessage(), type);
}
catch
{
continue;
}
}
}
}
The above example will call the static ImplementsMessage method on each class that is discovered that is a subclass of MessageBase.
This is well and goodly, but the problems start when you try and declare the ImplementsMessage in either an Interface or superclass. In this case, I have an Interface IMessage that I want all message handlers to implement. Interfaces and base classes are good, as we know, for defining a concrete contract to ensure that all of our classes implement the methods expected of them by the rest of the code - and are a key part of the design of this library.
Unfortunately, in any of the .NET languages - you cannot define static members in an object being inherited. Whilst the CLR can handle this, it's a design decision made by the .NET team over at Microsoft to not support it in their languages, though it seems that this is one they're looking at changing.
OK. So we can't define a static method, which isn't nice and elegant - but surely we can drop the static constraint and use the above code? Well, err... perhaps
In .NET, the CLR transparently sends an object reference to every method call made. The obvious exceptions are calls to a constructor when instantiating a new method, and calls to static methods. So it's imperative in the case above that we can always instantiate the object before we call the ImplementsMessage method.
This seems well and good, until you realise that .NET does not guarantee a default constructor as used in the example above. No, really - it doesn't. This might sound contradictory to some people, as the C# compiler will automatically create one for you if you use it on a fully qualified type at compile-time, but not at runtime.
That means the above sample will only work in the case you have explicitly defined a sane default constructor on your class at compile time. This is bad because we can't enforce this implementation with inheritance - so we're in the same problem as before: programming by convention rather than contract.
Fortunately there's a solution, and it's nowhere near as long winded as the explanation up until now. I cheekily hinted at it at the start of the article, and the answer is Custom Attributes.
Attributes are metadata you can attach to structural objects in .NET assemblies that can be reflected at runtime. If you're doing .NET, you've used them without realising. They're used heavily by components to provide information for the Visual Studio designer, are used to assign versioning and copyright information to the final assembly files as well as many other places within the framework.
By creating a custom attribute class, you can mark your classes, structs, enumerations and the like with strongly typed data that is available at runtime. This sounds like an excellent solution to the problem at hand! Here's the custom attribute class I wrote along with the updated code fragment to register these types at runtime:
[AttributeUsage(AttributeTargets.Class,AllowMultiple= true,Inherited=false)]
public sealed class HandlesMessage : Attribute
{
readonly MessageType msgType;
public HandlesMessage( MessageType inType )
{
this.msgType = inType;
}
public MessageType HandledType
{
get
{
return this.msgType;
}
}
}
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
foreach (Type type in assembly.GetTypes())
{
if( type.IsSubclassOf( typeof(MessageBase) ) )
{
// Look for the attribute saying what message they handle
foreach (Attribute attr in type.GetCustomAttributes(false))
{
if (attr.GetType().Equals(typeof(HandlesMessage)))
{
HandlesMessage hAttr = (HandlesMessage)attr;
_messageHandlers.Add(hAttr.HandledType, type);
break;
}
}
}
}
}
And here's a message handler being tagged:
[HandlesMessage(MessageType.InterfaceConfiguration)]
public class InterfaceConfigurationMessage : MessageBase
{
// ...
}
And there you have it! It's a different paradigm, but still enforces the message to implement to the MessageBase contract as well as having to "register" to be picked up at runtime.
The message class itself will have to implement a few interface methods from IMessage to construct new objects, so in this case it's arguable that that might be just as valid a solution - but that has its own caveats.
For now, it's an elegant and simple way of registering message handlers in the application without relying on convention to ensure the message handler will behave as expected - and I've already had another opportunity to use custom attributes at my work to maintain backwards compatibility with some code that is being backported.
That's all for now - a more general update to come and no doubt some more C# and .NET examples too, as well as some iOS related items.
- Read more about Runtime handler registration in .NET
- will's blog
- Log in or register to post comments
The Case FOR Apple
In reply to Jason Calacanis's recent post entitled The case against Apple - in Five Parts - one of my coworkers asked for my input. What follows is my somewhat lengthy reply which I thought I would share with everyone.
Jason has some good arguments, but I think he’s essentially missing the precepts of Apple’s design (as I see it):
- It has to be immensely intuitive and simple to use
- It has to be a masterpiece of industrial design
Specifically looking at the case of the iPod, it does one thing (plays music) and does it well.
There’s no need for it to do anything else, because they’re not aiming at that market – the product perfectly matches the (simple) set of requirements.
If you have different needs, there are still a ton of competing products out there to cater to your specific needs. This is almost a perfect example of a competitive market.
Also, if people are worried about their music and DRM/licensing – they should choose to buy it from a DRM-free location where they can download it. Actually come to think of it, Apple already offers DRM-free music from some labels. Don’t hate on them for not doing this originally, it’s a near certainty that they didn’t have the market power with the labels when they first launched.
With the iPhone the argument is pretty similar, however it’s important to note that Apple must do what they can to protect their investment. Part of the beauty of this product from a technical point of view, is that the software and hardware configurations are the same everywhere. It’s the same argument as with video game consoles, it works because the manufacturer puts things in place to make it easy for developers to deploy for it.
If Apple allowed a bajillion web browsers for example, what would happen when Opera is installed and is the default browsing app? Would other applications need to support launching Opera, would it just do it automatically? If so, can Opera handle all the iPhone specific display tags? It’s in their best interest to protect this investment, and to a certain point I can agree with the restricted platform – remembering that convergence be damned, its primary function is still being a telephone.
I think it’s laughable that people can make the argument that we’re headed towards an Apple monoculture – it’s not Apple’s responsibility to create competition, it’s the competitors who need to step up.
Further to this, have a look at what Apple’s innovation on the iPod and iPhone’s respective markets! Their product is setting new usability and design standards and injecting new and fresh ideas on how to make a simple mp3 player work better, and the number of yum-cha rip-offs reflect this.
In summary, I think it’s business as usual and from my perspective I don’t think Apple are doing anything inherently evil – in fact, I think we should congratulate them for bringing a brilliant product to market – their sales figures clearly indicate they’re doing something right.
- Read more about The Case FOR Apple
- will's blog
- Log in or register to post comments
On projects and discovery
It's been a while since I've posted, though on the whole I've been happy with the subject matter and quality of my posts recently - it seems I'm managing to be doing a lot more of the things that interest me, perhaps this is a good sign that things are well in my world, or maybe I've just reached a point of delusion where I'm too far detached from reality to notice. Either way, things are coming up Milhouse!
Since I last posted, I've moved from living in a fairly decrepit share house to sharing a lovely brand new villa with two reasonable people I can deal with on a daily basis - let's hope it stays that way, as I'm starting to feel at home for the first time in three years. It might have something to do with the fact that I have space to hang up my clothes properly and to unpack my bookshelf (yay). Not to mention that I've finally got space and the (kind of) right mental mindset to go through all my plastic tubs of hoarded junk and ditch it all (10 tubs down, 15 to go!)
Recently my out-of-work time and sanity has been wrapped up in a project I've come to realise was a really bad decision, and once I can clean my hands of it - I'll be able to start enjoying my evenings again, and will be able to pursue some more projects of interest. I'm reading into Operating System design theory in my spare time at work, and have even cobbled together the rough basics of my own bootable system that can write to the console. It's a bit on the sad side, but my brain definately feels engaged - which again, is a nice change.
In three weeks I'll be attending my first AusNOG Conference in Sydney, which should be a blast and pretty interesting. If I can knock off my failed project and sort out some outstanding issues, I should be able to use the extra time I've booked in Sydney to unwind and get rid of this stress-induced skin irritation - lovely. I've also decided that next year I'm going to plan to attend The Gathering, one of the most awesome demo parties around in Europe, as well as DEFCON, THE security conference.
On a completely unrelated note, I'd like to plug Adam Frisby's blog. I went to school with Adam and have kept somewhat in touch since, we have fairly similar interests - but he's always working on something interesting and his blog posts are a brilliant read.
I'm also hoping to sit down at some stage and finalise a layout for this site - it irks me that I'm using someone elses design, yet I'm never happy with anything I come up with. Oh well, happy days.
- Read more about On projects and discovery
- will's blog
- Log in or register to post comments
Pages