简体   繁体   English

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

[英]Type constrained open generics do not work with RegistrationBuilder

The code below does not work when RegistrationBuilder is used. 使用RegistrationBuilder时,下面的代码不起作用。 When the RegistrationBuilder is not added to the AssemblyCatalog constructor, type constrained generics work. 如果未将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
{}

First of all let's describe what exactly causes this behavior. 首先让我们描述究竟是什么导致了这种行为。

The RegistrationBuilder wraps the actual types of the assembly in a proxy type called CustomType. RegistrationBuilder将程序集的实际类型包装在名为CustomType的代理类型中。 This proxy more or less only exists to give the RegistrationBuilder the opportunity to inject the Export and Import attributes on the fly. 这个代理或多或少只是为了给RegistrationBuilder提供动态注入Export和Import属性的机会。

Sadly this Proxy also return wrapped types when you call GetGenericParameterConstraints. 遗憾的是,当您调用GetGenericParameterConstraints时,此Proxy也会返回包装类型。 So it is not a RuntimType IItem you get it's a CustomType IItem. 所以它不是RuntimType IItem你得到它的CustomType IItem。 When you try to get an export for IConstrained the AssemblyCatalog checks a lot of things to tell if your export matches you import. 当您尝试获取IConstrained的导出时,AssemblyCatalog会检查很多事情,以确定导出是否与导入匹配。 One of these checks is if the generic type constraint is satisfied. 其中一项检查是否满足泛型类型约束。 It's more or less a check like this. 它或多或少是这样的支票。 (Simplified) (简体)

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

The IsAssignableForm method of the CustomType is implemented like this. 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);
}

It only works if you pass another proxy type. 它仅在您传递另一种代理类型时有效。

I really think that's a major bug of the RegistrationBuilder and you should report it to Microsoft Connect. 我真的认为这是RegistrationBuilder的一个主要错误,你应该将它报告给Microsoft Connect。

To work around this issue you have to unproject the GenericTypeContraints saved with your ComposablePartDefinition. 要解决此问题,您必须取消使用ComposablePartDefinition保存的GenericTypeContraints。

Bad news is that all the relevant classes are internal so you cannot just override the GetGenericParameterConstraints Method. 坏消息是所有相关的类都是内部的,所以你不能只重写GetGenericParameterConstraints方法。

I solved this issue by inheriting the AssemblyCatalog and unprojecting the constraint types manually. 我通过继承AssemblyCatalog并手动取消投影约束类型来解决了这个问题。

public class MyAssemblyCatalog : AssemblyCatalog { private Func unprojectDelegate; 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);
}

} }

Now the Test works. 现在测试工作。

[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);
}

Simplier solution: 更简单的解决方案:

/// <summary>
/// When RegistrationBuilder is used, there is problem with Generics constraints - in produced ExportDefinition is generics constraint with descriptior CustomType which is not comparable with Type. 
/// * so composition failed on Export not found exception.
/// http://stackoverflow.com/questions/24590096/type-constrained-open-generics-do-not-work-with-registrationbuilder
/// </summary>
public static class PatchCatalogForRegistrationBuilderBug
{
    public static void FixCatalogForRegistrationBuilderBug(this ComposablePartCatalog catalog)
    {
        foreach (var item in catalog)
        {
            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++)
                        {
                            if (((object)types[i]).GetType().FullName != "System.Reflection.Context.Custom.CustomType") continue; //cast to object is only for due to R# warning
                            types[i] = types[i].UnderlyingSystemType;
                        }
                    }
                }
            }
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM