2011-11-04 65 views
2

让我首先说我是有限的MEF经验,并且之前使用Castle和Unity完成了我的目标。我希望可以用MEF做类似的事情。我可以使用MEF创建一个可扩展的工厂吗?

总之,我需要的是一个工厂类,可以通过名称实例化对象。更具体而言,我将有一个抽象基类如:

public abstract class TheBaseClass { ... } 

将有从基类派生的任何数量的子类:

public class OneSubClass : TheBaseClass { ... } 

public class AnotherSubClass : TheBaseClass { ... } 

在运行时,我需要一个工厂,我可以打电话,传递一个“关键”值,以获得特定的子类的实例返回,如:

var key = "AnotherSubClass"; 
TheBaseClass instance = TheFactory.CreateInstance(key); 

在城堡和团结,我可以注册与“钥匙”作为名称的类型和使用这个值作为查找当试图从容器中解析实例时。我认为我可以使用ExportMetadata完成同样的事情,但我仍然坚持如何做到这一点。

这种方法背后的基本原理是,我需要在运行时实例化一个强类型的子类,而不必在编译时知道该类型,因为应用程序是可扩展的和(导出的)类型可以通过外部组件添加。

任何想法?

回答

2

我建议使用强类型名称以避免错误输入错误。

要做到这一点,首先你需要创建,你将作为重点使用枚举:

public enum DerivedClassesKeyEnum 
{ 
    ONE, 
    TWO 
} 

然后创建一个自定义属性:

[MetadataAttribute] 
[AttributeUsage(AttributeTargets.Class)] 
public class DirivedBaseExportAttribute : ExportAttribute 
{ 
    public DirivedBaseExportAttribute() 
     :base(typeof(TheBaseClass)) 
    { } 

    public DerivedClassesKeyEnum DerivedClassId { get; set; } 
} 

接下来,在应用此属性to yuor derived classes:

[DirivedBaseExport(DerivedClassId=DerivedClassesKeyEnum.ONE)] 
public class OneSubClass : TheBaseClass 
{ 

} 

在将导入这些类的部分中,您声明了一个inter脸:

public interface IDerivedClassMetadata 
{ 
    DerivedClassesKeyEnum DerivedClassId{get;} 
} 

而最后一点,在你的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; 
    } 

} 

它被简化代码只是为了说明用法。

+0

虽然我明白枚举的价值,以消除“魔术字符串”的枚举打破了松散耦合(可扩展)模型,因为我不得不添加一个新的字段的枚举每次我想扩展工厂,而不是简单地下降汇编到运行时目录并允许发现填充枚举。除此之外,您的解决方案完全适合我。谢谢! – SonOfPirate

2

这在MEF中的工作方式与在您提及的其他IoC容器中的工作方式相同。

[Export("one", typeof(TheBaseClass)] 
public class OneSubClass : TheBaseClass { ... } 

[Export("two", typeof(TheBaseClass)] 
public class AnotherSubClass : TheBaseClass { ... } 

的“钥匙”我已经在这里指定为"one""two"当然你可以使用任何你喜欢的。

然后,您使用组合键与GetExport()

var catalog = new TypeCatalog(typeof(OneSubClass), typeof(AnotherSubClass)); 
var container = new CompositionContainer(catalog); 

var two = container.GetExport<TheBaseClass>("two"); 
var value = two.Value; 

有两点要注意;不要忘记以这种方式释放您从容器中获得的出口,例如使用container.ReleaseExport(two)

您还应该注意,您可以将其用于任何目录 - 我刚刚选择TypeCatalog作为示例,但其他工作方式也同样适用。

+0

我更喜欢Slava的解决方案,因为我尽量避免在初始化代码之外引用容器。也就是说,使用ExportAttribute上的ContractName属性当然是所有需要使用元数据的工作的可行选项 - 只要我们只关心传递单个字符串值即可。感谢您的回应。 – SonOfPirate

相关问题