So last night I implemented the ideas from my last post, in that I turned my ICollectionWriter interface into an ICollectionReaderWriter interface, and derived it from ICollectionReader. So now if you want to create a class that can write a collection to a stream, you’re forced to also implement the ability to read it back in[1]. This’ll stop people from accidentally saving their work to a file that Comicster can’t open – something that would aggravate new users.

While refactoring the code, I learned a whole lot more about MEF. Let me explain with some code.

In my Comicster.IO.dll assembly, I define two classes (PackageCollectionReader and XmlCollectionReader), which serve as the “default” file formats for Comicster. I “export” them using this MEF attribute:

[Export(typeof(ICollectionReaderWriter))]
public class PackageCollectionReaderWriter : ICollectionReaderWriter
{
    ...
}

Then at the top of my main window’s class I have two properties which get automatically populated by MEF. They import the classes they need using the interface types, like this:

[Import]
public static IList<ICollectionReader> CollectionReaders
{
    get; set;
}

[Import]
public static IList<ICollectionReaderWriter> CollectionReaderWriters
{
    get; set;
}

So because CollectionReaders is an IList of ICollectionReader, MEF knows to go find all the classes that implement ICollectionReader and fill the list with them. Likewise with CollectionReaderWriters.

Here’s where it gets a bit tricky.

Even though ICollectionReaderWriter derives from ICollectionReader, MEF won’t import my PackageCollectionReaderWriter class into the “CollectionReaders” property. The importing procedure is very tightly bound to the specified type, and doesn’t look at ancestor types at all. I can see why – how far up the ancestral tree do you go? You would eventually work your way back to System.Object which would mean that anything could be imported.

Still, I was interested in looking at the MEF source code to see if I could determine how MEF discovers the classes based on their type. In the spirit of Scott Hanselman’s Weekly Source Code, I went spelunking.

It turns out MEF’s exporting and importing is fundamentally based on strings. Each export is given a string to identify it, and imports look for exported types with a matching string. When you supply an [Export] attribute with a type (as I have done above), MEF simply calls ToString() on the type and assigns that to the export. So this line:

[Export(typeof(ICollectionReaderWriter))]

… is fundamentally the same as this:

[Export("Comicster.IO.ICollectionReaderWriter")]

All of a sudden it becomes crystal clear why MEF doesn’t discover types based on their inheritance hierarchy – by the time MEF does its lookups, they’re no longer types: they’re just strings.

So how did I get around the problem of my ICollectionReaderWriter classes not being treated as ICollectionReaders? Well, in the end I worked around it by combining the two lists with a little LINQ statement when I needed them:

var allReaders = CollectionReaderWriters.Cast<ICollectionReader>().Concat(CollectionReaders);

It was a bit of fun trawling through the MEF source, and it’s a testament to the developers that a nincompoop such as myself is able to understand what they’re doing under the hood. Look for more MEF posts in the future as I start to use it in more places!

[1] Ok, I know that nobody is ever forced to correctly implement an interface. There’s nothing stopping you from throwing a NotImplementedException or simply returning null. There’s not a whole lot I can do about that, but this is mostly about guiding the plug-in authors in the right direction rather than imposing rules.