问题
Let me start by saying that I am coming at this with limited MEF experience and have accomplished my goals using both Castle and Unity previously. I'm hoping something similar can be done with MEF.
In short, what I need is a factory class that can instantiate objects by name. More specifically, I will have an abstract base class such as:
public abstract class TheBaseClass { ... }
There will be any number of subclasses that derive from the base class:
public class OneSubClass : TheBaseClass { ... }
public class AnotherSubClass : TheBaseClass { ... }
At runtime, I need a factory that I can call, passing a 'key' value, to get the specific subclass instance returned like:
var key = "AnotherSubClass";
TheBaseClass instance = TheFactory.CreateInstance(key);
In Castle and Unity, I could register the type with 'key' as the name and use this value as a lookup when trying to resolve the instance from the container. I thought I might be able to accomplish the same thing using ExportMetadata but am at a stand-still how I can do it.
The rationale behind this approach (in case there's a better way), is that I need to instantiate a strongly-typed subclass at runtime without any knowledge of that type at compile time because the application is extensible and (exported) types can be added through external assemblies.
Any ideas?
回答1:
I would suggest to use strongly typed names to avoid mistyping errors.
To do so, first you need to create an enum that you will use as a key:
public enum DerivedClassesKeyEnum
{
ONE,
TWO
}
Then you create a custom attribute:
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class)]
public class DirivedBaseExportAttribute : ExportAttribute
{
public DirivedBaseExportAttribute()
:base(typeof(TheBaseClass))
{ }
public DerivedClassesKeyEnum DerivedClassId { get; set; }
}
Next, you apply this attribute to yuor derived classes:
[DirivedBaseExport(DerivedClassId=DerivedClassesKeyEnum.ONE)]
public class OneSubClass : TheBaseClass
{
}
In the part that will import these classes you declare an interface:
public interface IDerivedClassMetadata
{
DerivedClassesKeyEnum DerivedClassId{get;}
}
And last bit, in your FactoryClass:
public class TheFactory
{
[ImportMany]
public static IEnumerable<Lazy<TheBaseClass, IDerivedClassMetadata>> DerivedClasses { get; set; }
public static TheBaseClass CreateInstance(DerivedClassesKeyEnum id)
{
return DerivedClasses.Single(c => c.Metadata.DerivedClassId == id).Value;
}
}
It is simplified code just to illustrate the usage.
回答2:
this works the same way in MEF as it does in the other IoC containers you mentioned.
[Export("one", typeof(TheBaseClass)]
public class OneSubClass : TheBaseClass { ... }
[Export("two", typeof(TheBaseClass)]
public class AnotherSubClass : TheBaseClass { ... }
The 'keys' I've assigned here are "one"
and "two"
but of course you can use anything you like.
Then, you use that key in combination with GetExport()
:
var catalog = new TypeCatalog(typeof(OneSubClass), typeof(AnotherSubClass));
var container = new CompositionContainer(catalog);
var two = container.GetExport<TheBaseClass>("two");
var value = two.Value;
A couple of notes; don't forget to release the exports you get from the container in this way, using for example container.ReleaseExport(two)
.
You should also note that you can use this with any catalog - I've just chosen TypeCatalog
for the example, but others work equally well.
来源:https://stackoverflow.com/questions/8009107/can-i-use-mef-to-create-an-extensible-factory