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

时间:2014-07-05 19:58:05

标签: .net .net-4.5 mef convention open-generics

使用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
{}

2 个答案:

答案 0 :(得分:3)

首先让我们描述究竟是什么导致了这种行为。

RegistrationBuilder将程序集的实际类型包装在名为CustomType的代理类型中。这个代理或多或少只是为了给RegistrationBuilder提供动态注入导出和导入属性的机会。

遗憾的是,当您调用GetGenericParameterConstraints时,此Proxy也会返回包装类型。所以它不是RuntimType IItem你得到它的CustomType IItem。当您尝试获取IConstrained的导出时,AssemblyCatalog会检查很多事情,以确定导出是否与导入匹配。其中一项检查是否满足泛型类型约束。它或多或少是这样的支票。 (简化的)

exportToCheck.GenericTypeConstraints[0].IsAssignableFrom(typeof(Item))

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

public override bool IsAssignableFrom(Type c)
{
    ProjectingType projectingType = c as ProjectingType;
    return !(projectingType == null) && this.Projector == projectingType.Projector && 
              base.UnderlyingType.IsAssignableFrom(projectingType.UnderlyingType);
}

仅在传递其他代理类型时才有效。

我真的认为这是RegistrationBuilder的一个主要错误,你应该将它报告给Microsoft Connect。

要解决此问题,您必须取消使用ComposablePartDefinition保存的GenericTypeContraints。

坏消息是所有相关的类都是内部的,所以你不能只是覆盖GetGenericParameterConstraints方法。

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

public class MyAssemblyCatalog:AssemblyCatalog {     private Func unprojectDelegate;

private bool projectionsChecked = false;

public MyAssemblyCatalog(Assembly assembly, CustomReflectionContext reflectionContext)
    : base(assembly, reflectionContext)
{
    this.ReflectionContext = reflectionContext;
}

public CustomReflectionContext ReflectionContext { get; private set; }

public Type Unproject(Type type)
{
    if (this.unprojectDelegate == null) {
        var param = Expression.Parameter(typeof(CustomReflectionContext));
        var param2 = Expression.Parameter(typeof(Type));
        var prop = Expression.Property(param, param.Type.GetProperty("Projector", BindingFlags.Instance | BindingFlags.NonPublic));
        var method = prop.Type.GetMethod("Unproject", BindingFlags.Instance | BindingFlags.Public, null, new[] { typeof(Type) }, null);
        var body = Expression.Call(prop, method, param2);
        this.unprojectDelegate = Expression.Lambda<Func<CustomReflectionContext, Type, Type>>(body, param, param2).Compile();
    }
    return unprojectDelegate(this.ReflectionContext, type);
}

private void EnsureUnprojectedGenericTypeConstraints()
{
    if (!this.projectionsChecked) {
        foreach (var item in this) {
            object value1;
            if (item.Metadata.TryGetValue("System.ComponentModel.Composition.GenericParameterConstraints", out value1)) {
                var items = (object[])value1;
                foreach (var entry in items) {
                    var types = entry as Type[];
                    if (types != null) {
                        for (int i = 0; i < types.Length; i++) {
                            types[i] = Unproject(types[i]);
                        }
                    }
                }
            }
        }
        projectionsChecked = true;
    }
}

public override System.Collections.Generic.IEnumerable<Tuple<ComposablePartDefinition, ExportDefinition>> GetExports(ImportDefinition definition)
{
    EnsureUnprojectedGenericTypeConstraints();
    return base.GetExports(definition);
}

}

现在测试工作。

[TestMethod]
public void TypeConstraintTest()
{
    var rb = new RegistrationBuilder();

    var a = new MyAssemblyCatalog(Assembly.GetExecutingAssembly(), rb);

    var aggr = new AggregateCatalog(a);
    var c = new CompositionContainer(aggr);
    var item = c.GetExportedValue<IConstrained<Item>>();

    Assert.IsNotNull(item);
}

答案 1 :(得分:0)

更简单的解决方案:

android.app.AlertDialog