【问题标题】:MEF 2 can't import generic implementation of generic interfaceMEF 2 无法导入通用接口的通用实现
【发布时间】:2013-02-19 20:39:12
【问题描述】:

我正在使用来自Codeplex homepage 的最新 MEF 2 预览版,它应该添加开放泛型支持。确实如此,但在这种特定情况下,MEF 无法组合泛型接口的泛型实现:

public interface IOuter
{
    IInner Value { get; }
}

[Export(typeof(IOuter))]
public class MyOuter : IOuter
{
    [ImportingConstructor]
    public MyOuter(InnerGenericClass<string, int> value)
    {
        this.Value = value;
    }

    public IInner Value { get; private set; }
}

public interface IInner
{
    void Say();
}
public interface IGenericInner<T, K> : IInner
{
    // something else here
}

[Export(typeof(IGenericInner<,>))]
public class InnerGenericClass<T, K> : IGenericInner<T, K>
{
    public void Say()
    {
        Console.WriteLine("{0}, {1}", typeof(T), typeof(K));
    }
}

class Startup
{
    public void CatalogSetup()
    {
        var catalog = new AggregateCatalog(
            new AssemblyCatalog(Assembly.GetExecutingAssembly())
            );
        var container = new CompositionContainer(catalog, CompositionOptions.DisableSilentRejection);

        var batch = new CompositionBatch();

        container.Compose(batch);

        var outer = container.GetExportedValue<IOuter>();
        outer.Value.Say();
    }
}

这是CompositionExpection

The composition produced a single composition error. The root cause is provided below. Review the CompositionException.Errors property for more detailed information.

1) No exports were found that match the constraint: 
    ContractName    ConsoleApplication1.InnerGenericClass(System.String,System.Int32)
    RequiredTypeIdentity    ConsoleApplication1.InnerGenericClass(System.String,System.Int32)

Resulting in: Cannot set import 'ConsoleApplication1.MyOuter.Value (ContractName="ConsoleApplication1.InnerGenericClass(System.String,System.Int32)")' on part 'ConsoleApplication1.MyOuter'.
Element: ConsoleApplication1.MyOuter.Value (ContractName="ConsoleApplication1.InnerGenericClass(System.String,System.Int32)") -->  ConsoleApplication1.MyOuter -->  AssemblyCatalog (Assembly="ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")

Resulting in: Cannot get export 'ConsoleApplication1.MyOuter (ContractName="ConsoleApplication1.IOuter")' from part 'ConsoleApplication1.MyOuter'.
Element: ConsoleApplication1.MyOuter (ContractName="ConsoleApplication1.IOuter") -->  ConsoleApplication1.MyOuter -->  AssemblyCatalog (Assembly="ConsoleApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null")

即使我将 InnerGenericClass 的导入移动到 MyOuter.Value 的属性,也会引发相同的异常:

[Export(typeof(IOuter))]
public class MyOuter : IOuter
{
    [Import(typeof(InnerGenericClass<string, int>))]
    public IInner Value { get; private set; }
}

奇怪的是,当我将导入类型更改为 IGenericInner 时,它确实有效

[Export(typeof(IOuter))]
public class MyOuter : IOuter
{
    [ImportingConstructor]
    public MyOuter(IGenericInner<string, int> value)
    {
        this.Value = value;
    }

    public IInner Value { get; private set; }
}

更奇怪的是,当通过属性导入时,它不起作用

总结:我不能使用通用接口将对象导入到Value属性,因为IGenericInner接口可能有更多的实现(我还想导入一个特定的实现,但这并不重要)。

我希望在这种情况下我不必完全绕过 MEF。

【问题讨论】:

    标签: c# generics .net-4.0 mef


    【解决方案1】:

    导致您导出接口 IGenericInner 但要导入特定类 InnerGenericClass MEF 没有找到正确的部分。您可以通过这种方式导出和导入特定类或创建两个 InnerGenericClass 导出:

    [Export(typeof(IGenericInner<,>))]
    [Export(typeof(InnerGenericClass<,>))]
    public class InnerGenericClass<T, K> : IGenericInner<T, K> {
        public void Say() {
            Console.WriteLine("{0}, {1}", typeof(T), typeof(K));
        }
    }
    

    我刚刚在 VS .NET 4.5 项目中对此进行了测试,它可以工作。 顺便提一句。 MEF 2 已在 .NET 4.5 中发布 - 如果可能,我建议使用框架的 System.ComponentModel.Composition.dll 部分而不是预览版。

    更新: 第三种解决方案(适用于预览版 5)可能是另外使用字符串合同名称。我个人尽量避免这种难看语法的原因,但没关系。 代码将如下所示:

    [Export(typeof(IOuter))]
    public class MyOuter : IOuter {
        [ImportingConstructor]
        public MyOuter([Import("MySpecialInnerGenericClass", typeof(IGenericInner<,>))]InnerGenericClass<string, int> value) {
            this.Value = value;
        }
    
        public IInner Value { get; private set; }
    }
    [Export("MySpecialInnerGenericClass", typeof(IGenericInner<,>))]
    public class InnerGenericClass<T, K> : IGenericInner<T, K> {
        public void Say() {
            Console.WriteLine("{0}, {1}", typeof(T), typeof(K));
        }
    } 
    

    【讨论】:

    • 不幸的是,我只使用了 4.0(见问题标签)。您的建议不适用于预览版。而且我无法升级到 4.5,因为我还有运行 WinXP 的客户端。
    • 嗯,太糟糕了。我添加了第三个选项。我尽量避免这种语法,但它解决了问题。
    • 谢谢,但这确实看起来很难看。我想我不会将 MEF 用于我的 API 的这个特定部分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-22
    • 1970-01-01
    • 2010-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多