Instead of thinking purely in terms of MEF, look at your class design in a broader sense. Typically when you are designing a class, you have a set of associated properties, these could be services, e.g.,
public class MyService
{
public ILogger Logger { get; set; }
public void SaySomething()
{
Logger.Log("Something");
}
}
Now, I could go ahead and create an instance of that:
var service = new MyService();
And now, if I try and use the method:
service.SaySomething();
If I don't know explicitly that I have to also initialise my Logger
property:
var service = new MyService() { Logger = new ConsoleLogger() };
(or):
var service = new MyService();
service.Logger = new ConsoleLogger();
Then the error won't become apparent until runtime. If we were to redefine the class:
public class MyService
{
private readonly ILogger _logger;
public MyService(ILogger logger)
{
if (logger == null) throw new ArgumentNullException("logger");
_logger = logger;
}
public void SaySomething()
{
_logger.Log("Something");
}
}
Now, if you try and create an instance of MyService
, you explicitly have to provide this additional service (ILogger
) for the object to be initialised correctly. This helps a number of ways:
- You express the dependencies your type requires, and this forms an initialisation contract that must be satisfied to ensure the type is created in a usable state.
- You decrease the risk of runtime errors by ensuring services are passed to your type.
You can improve on this design more by using Code Contracts (as mentioned by @JaredPar) to incude static checking at compile time.
In terms of MEF, you can get away with using [Import]
instead of [ImportingConstructor]
as MEF will throw an exception when it cannot satisfy all imports on a type, and will only return the type after both initialisation ([ImportingConstructor]
) and then [Import]
s.
Constructor injection is generally preferable.