【问题标题】:Type constrained open generics do not work with RegistrationBuilder类型受限的开放泛型不适用于 RegistrationBuilder
【发布时间】:2014-08-26 17:24:57
【问题描述】:

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

【问题讨论】:

  • 请详细说明,var rb = new RegistrationBuilder(); var a = new AssemblyCatalog(Assembly.GetExecutingAssembly(), rb); rb 没用过,什么都不做
  • 它什么也没做,但它改变了行为,也就是说,它破坏了开放的泛型。计划是在基本情况下使用它,只需将其传入即可。

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


【解决方案1】:

更简单的解决方案:

/// <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;
                        }
                    }
                }
            }
        }
    }
}

【讨论】:

    【解决方案2】:

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

    RegistrationBuilder 将程序集的实际类型包装在称为 CustomType 的代理类型中。这个代理或多或少只是为了让 RegistrationBuilder 有机会即时注入 Export 和 Import 属性。

    遗憾的是,当您调用 GetGenericParameterConstraints 时,此代理也会返回包装类型。所以它不是一个 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 并手动取消投影约束类型解决了这个问题。

    公共类 MyAssemblyCatalog :AssemblyCatalog { 私有函数 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);
    }
    

    【讨论】:

    • 感谢您的深入研究。出于好奇,您是如何看到 MEF 源代码的?通过 ILSpy 反编译还是找到了实际的源代码?
    • 当涉及到诸如“GetExports”在 Lazy 上调用 .Value(但仅在列表的最后一个)上时,此解决方案存在问题。也在 GetExportedValues 上。在 System.ComponentModel.Composition.ReflectionModel.GenericServices.CanSpecialize(Type type, IEnumerable`1 constraintTypes) 处的 System.Reflection.Emit.TypeBuilderInstantiation.IsAssignableFrom(Type c) 处获取 NotSupported 异常`
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-04
    • 1970-01-01
    相关资源
    最近更新 更多