Extending Caliburn.Micro
Caliburn.Micro has an interesting extensibility story. Personally, I'd never come across this technique before, and it took a little while for me to grok, but now that I do, I really like it.
To dig into how extensibility works, consider logging: Caliburn has an ILog interface that contains methods Info, Warn and Error as you might expect. Now have a look at the way logging is implemented by default:
First there is a concrete logger that implements ILog. As it happens, it doesn't do anything, but that's neither here nor there - it looks like this
private class NullLog : ILog
{
public void Info(string format, params object[] args) { }
public void Warn(string format, params object[] args) { }
public void Error(Exception exception) { }
}
Now that's not the interesting bit - the interesting bit is how the framework plugs this NullLog in by default - by exposing a static Func as a public field on a LogManager class.
Go and read that back - it's worth it. The LogManager class exposes a field (not a property) called GetLog that is a Func<Type, ILog> - it's a method that takes in a Type as a parameter and returns an ILog for that type, and by default it looks like this:
/// <summary>
/// Creates an <see cref="ILog"/> for the provided type.
/// </summary>
public static Func<Type, ILog> GetLog = type => NullLogInstance;
OK? And to swap out logging for your project? You just need to set the LogManager.GetLog field to be your own method that takes in a Type and returns in ILog for that type.
Maybe if you wanted to create a logger that just writes to the Output window, you'd create something like
class DebugLog : ILog
{
public void Info(string format, params object[] args)
{
System.Diagnostics.Debug.WriteLine(format, args);
}
// implement Warn and Error, etc...
}
and then somewhere, probably in the app bootstrapper:
LogManager.GetLog = (type) => new DebugLog();
The deeper you dig into the framework, the more you realise that more and more of the pieces are composed of these static Func methods - which means that whilst Convention over Configuration is working hard for us by default, we have a large amount of power to define those conventions...


Comments
kiwidev
I do like this technique of Rob's.
Very similar in many ways to the extensibility model of Javascript (functions being properties of an object and assignable like a variable).
Wonder if dynamics could be used in a very similar manner / simplify this even more?