类型约束开放泛型不适用于RegistrationBuilder

Rom*_*kin 5 .net convention mef open-generics .net-4.5

使用时,下面的代码不起作用RegistrationBuilder.如果RegistrationBuilder未将其添加到AssemblyCatalog构造函数中,请键入约束泛型工作.

[TestClass]
public class TypeConstraints
{
    [TestMethod]
    public void TypeConstraintTest()
    {
        var rb = new RegistrationBuilder();
        var a = new AssemblyCatalog(Assembly.GetExecutingAssembly(), rb);
        //var a = new AssemblyCatalog(Assembly.GetExecutingAssembly()); //Works!
        var aggr = new AggregateCatalog(a);
        var c = new CompositionContainer(aggr);
        var item = c.GetExportedValue<IConstrained<Item>>();
        Assert.IsNotNull(item);
    }
}

public interface IConstrained<T> where T : IItem
{}

[Export(typeof (IConstrained<>))]
public class Constrained<T> : IConstrained<T> where T : IItem
{}

public class Item : IItem
{}

public interface IItem
{}
Run Code Online (Sandbox Code Playgroud)

cod*_*orx 3

首先让\xe2\x80\x99s 描述到底是什么导致了这种行为。

\n\n

RegistrationBuilder 将程序集的实际类型包装在名为 CustomType 的代理类型中。该代理或多或少的存在只是为了让 RegistrationBuilder 有机会动态注入 Export 和 Import 属性。

\n\n

遗憾的是,当您调用 GetGenericParameterConstraints 时,此代理还会返回包装类型。所以它不是 RuntimType IItem,您得到的\xe2\x80\x99s 是 CustomType IItem。当您尝试导出 IConstrained 时,AssemblyCatalog 会检查很多内容来判断您的导出是否与导入相匹配。这些检查之一是是否满足泛型类型约束。它\xe2\x80\x99 或多或少是这样的检查。(简体)

\n\n
exportToCheck.GenericTypeConstraints[0].IsAssignableFrom(typeof(Item))\n
Run Code Online (Sandbox Code Playgroud)\n\n

CustomType的IsAssignableForm方法是这样实现的。

\n\n
public override bool IsAssignableFrom(Type c)\n{\n    ProjectingType projectingType = c as ProjectingType;\n    return !(projectingType == null) && this.Projector == projectingType.Projector && \n              base.UnderlyingType.IsAssignableFrom(projectingType.UnderlyingType);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

仅当您传递其他代理类型时它才有效。

\n\n

我确实认为 \xe2\x80\x99 是 RegistrationBuilder 的一个主要错误,您应该将其报告给 Microsoft Connect。

\n\n

要解决此问题,您必须取消投影与 ComposablePartDefinition 一起保存的 GenericTypeContraints。

\n\n

坏消息是所有相关类都是内部的,因此您不能只重写 GetGenericParameterConstraints 方法。

\n\n

我通过继承 AssemblyCatalog 并手动取消投影约束类型解决了这个问题。

\n\n

公共类 MyAssemblyCatalog : AssemblyCatalog\n{\n private Func unprojectDelegate;

\n\n
private bool projectionsChecked = false;\n\npublic MyAssemblyCatalog(Assembly assembly, CustomReflectionContext reflectionContext)\n    : base(assembly, reflectionContext)\n{\n    this.ReflectionContext = reflectionContext;\n}\n\npublic CustomReflectionContext ReflectionContext { get; private set; }\n\npublic Type Unproject(Type type)\n{\n    if (this.unprojectDelegate == null) {\n        var param = Expression.Parameter(typeof(CustomReflectionContext));\n        var param2 = Expression.Parameter(typeof(Type));\n        var prop = Expression.Property(param, param.Type.GetProperty("Projector", BindingFlags.Instance | BindingFlags.NonPublic));\n        var method = prop.Type.GetMethod("Unproject", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(Type) }, null);\n        var body = Expression.Call(prop, method, param2);\n        this.unprojectDelegate = Expression.Lambda<Func<CustomReflectionContext, Type, Type>>(body, param, param2).Compile();\n    }\n    return unprojectDelegate(this.ReflectionContext, type);\n}\n\nprivate void EnsureUnprojectedGenericTypeConstraints()\n{\n    if (!this.projectionsChecked) {\n        foreach (var item in this) {\n            object value1;\n            if (item.Metadata.TryGetValue("System.ComponentModel.Composition.GenericParameterConstraints", out value1)) {\n                var items = (object[])value1;\n                foreach (var entry in items) {\n                    var types = entry as Type[];\n                    if (types != null) {\n                        for (int i = 0; i < types.Length; i++) {\n                            types[i] = Unproject(types[i]);\n                        }\n                    }\n                }\n            }\n        }\n        projectionsChecked = true;\n    }\n}\n\npublic override System.Collections.Generic.IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>> GetExports(ImportDefinition definition)\n{\n    EnsureUnprojectedGenericTypeConstraints();\n    return base.GetExports(definition);\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

}

\n\n

现在测试有效了。

\n\n
[TestMethod]\npublic void TypeConstraintTest()\n{\n    var rb = new RegistrationBuilder();\n\n    var a = new MyAssemblyCatalog(Assembly.GetExecutingAssembly(), rb);\n\n    var aggr = new AggregateCatalog(a);\n    var c = new CompositionContainer(aggr);\n    var item = c.GetExportedValue<IConstrained<Item>>();\n\n    Assert.IsNotNull(item);\n}\n
Run Code Online (Sandbox Code Playgroud)\n