1

我无法弄清楚正确的使用方法RegisterAllOpenGeneric

我有这些简单的定义:

public interface ISubscribeTo<T> { }
public class AnEventOf<T> { }

public interface IMarker { }
public class PocoB : IMarker { }

和一个正常 Subscriber的:

public class SubscribeToPocoB : ISubscribeTo<AnEventOf<PocoB>>
{
}

使用以下代码注册:

private void RegisterSubscribers(Container container)
{
    var implementations = new List<Type>();

    container.RegisterManyForOpenGeneric(typeof(ISubscribeTo<>),
        AccessibilityOption.PublicTypesOnly,
        (serviceType, implTypes) =>
        {
            container.RegisterAll(serviceType, implTypes);

            implementations.AddRange(implTypes);
        },
        AppDomain.CurrentDomain.GetAssemblies()
    );

    implementations
        .Distinct()
        .ToList()
        .ForEach(type => container.Register(type));

    container.Verify();
}

并在调用时返回:

var registrations = container
            .GetAllInstances<ISubscribeTo<AnEventOf<PocoB>>>();

到目前为止一切都很好。

PocoB还实现了接口IMarker,我正在尝试创建一个SubscriberAnEventOf<IMarker>>调用GetAllInstances<ISubscribeTo<AnEventOf<PocoB>>>();.

我尝试了 3 种不同的定义:

public class SubscribeToIMarker1<TMarker> : ISubscribeTo<AnEventOf<TMarker>>
    where TMarker : IMarker
{
}

public class SubscribeToIMarker2<TAnEventOf> : ISubscribeTo<TAnEventOf>
    where TAnEventOf : AnEventOf<IMarker>
{
}

public class SubscribeToIMarker3<TMarker> : ISubscribeTo<TMarker>
    where TMarker : IMarker
{
}

这是我编写的不同测试方法 - 没有一个测试有效,它们都只返回SubscribeToPocoB

[Test]
public void GetAllInstances_PocoB1_ReturnTwoRegistrations()
{
    Container container = new Container();

    container.RegisterAllOpenGeneric(
        typeof(ISubscribeTo<>),
        typeof(SubscribeToIMarker1<>));

    RegisterSubscribers(container);

    var registrations = container
        .GetAllInstances<ISubscribeTo<AnEventOf<PocoB>>>();

    Assert
        .That(registrations.Count(),
            Is.EqualTo(2));
}

[Test]
public void GetAllInstances_PocoB2_ReturnTwoRegistrations()
{
    Container container = new Container();

    container.RegisterAllOpenGeneric(
        typeof(ISubscribeTo<>),
        typeof(SubscribeToIMarker2<>));

    RegisterSubscribers(container);

    var registrations = container
        .GetAllInstances<ISubscribeTo<AnEventOf<PocoB>>>();

    Assert
        .That(registrations.Count(),
            Is.EqualTo(2));
}

[Test]
public void GetAllInstances_PocoB3_ReturnTwoRegistrations()
{
    Container container = new Container();

    container.RegisterAllOpenGeneric(
        typeof(ISubscribeTo<>),
        typeof(SubscribeToIMarker3<>));

    RegisterSubscribers(container);

    var registrations = container
        .GetAllInstances<ISubscribeTo<AnEventOf<PocoB>>>();

    Assert
        .That(registrations.Count(),
            Is.EqualTo(2));
}

我是否需要做更多的事情才能正确注册所有开放的泛型(例如SubscribeToIMarker1)?

container.RegisterAllOpenGeneric(
    typeof(ISubscribeTo<>),
    typeof(SubscribeToIMarker1<>));
4

1 回答 1

1

是该方法RegisterAllOpenGeneric的集合等价物RegisterOpenGeneric。这意味着您可以将一组开放的泛型类型注册为一个集合。然而,与该方法一样,只有在没有显式注册时才会解析RegisterOpenGeneric注册的集合。RegisterAllOpenGeneric

在您的情况下,您通过调用来显式注册集合container.RegisterAll(Type, Type[])。由于您显式注册了此类型,因此不会构建已注册的开放泛型类型集合。

这很好,但是由于您的显式集合注册不包含任何开放的泛型类型,因此解析的集合将仅包含那些已注册的非泛型类型。注册一组开放的泛型类型RegisterAllOpenGeneric并不会自动将它们包含在任何RegisterAll注册中。

所以解决方案是也注册开放的泛型类型。但是,您不能将开放的泛型类型添加到RegisterAll方法中,因为此方法只接受封闭的泛型类型和非泛型类型。诀窍是使用RegisterManyForOpenGeneric接受回调委托和 s 集合的重载Type。此重载接受开放的泛型类型并将封闭的泛型版本分发回回调委托。

这是您的RegisterSubscribers方法应如下所示:

private void RegisterSubscribers(Container container)
{
    Type[] openGenericSubscribers = new[]
    { 
        typeof(SubscribeToIMarker1<>),
        typeof(SubscribeToIMarker2<>),
        typeof(SubscribeToIMarker3<>)
    };

    var nonGenericSubscribers = OpenGenericBatchRegistrationExtensions
        .GetTypesToRegister(container,
            typeof(ISubscribeTo<>),
            AccessibilityOption.PublicTypesOnly,
            AppDomain.CurrentDomain.GetAssemblies());

    container.RegisterManyForOpenGeneric(typeof(ISubscribeTo<>),
        container.RegisterAll,
        nonGenericSubscribers.Concat(openGenericSubscribers));

    container.RegisterAllOpenGeneric(typeof(ISubscribeTo<>), 
        openGenericSubscribers);

    container.Verify();
}

这里发生的情况是,我们调用该方法来获取非通用订阅者列表,而不是调用RegisterManyForOpenGeneric接受对象集合的方法。接受集合的重载在幕后使用此方法。AssemblyOpenGenericBatchRegistrationExtensions.GetTypesToRegisterRegisterManyForOpenGenericAssembly

接下来,我们将非泛型订阅者列表提供给RegisterManyForOpenGeneric接受回调委托和Type对象集合的重载,但我们将打开的泛型订阅者连接到所提供类型的集合。

RegisterManyForOpenGeneric根据它们实现的封闭泛型接口对提供的非泛型类型列表进行ISubscribeTo<T>分组(如果它实现多个接口,则可能将类型放在多个组中ISubscribeTo<T>)。ISubscribeTo<T>构造组时,使用服务类型和分组的实现类型调用回调委托。

但是在调用回调委托之前,这个具体RegisterManyForOpenGeneric的做了最后一件事。它将迭代提供的开放泛型实现列表(SubscribeToIMarker1<T>在本例中为 , 2 和 3),并尝试基于封闭泛型版本构建封闭泛型ISubscribeTo<T>版本。这当然是根据给定的类型约束完成的。所有构建的封闭通用实现都将被连接到实现列表中。

这意味着最终container.RegisterAll将调用非泛型和封闭泛型类型。

不能神奇地从无到有地组成新的RegisterManyForOpenGeneric泛型类型,因此它只会调用具有封闭ISubscribeTo<T>服务类型的回调委托,他会为其找到一个实现封闭的非泛型实现ISubscribeTo<T>。所以这意味着这RegisterManyForOpenGeneric将不允许我们解析仅由封闭泛型类型组成的集合。为此,我们需要 the RegisterAllOpenGeneric,这就是 the 中的最后一条语句RegisterSubscribers。在没有明确注册集合的情况下,它使用完全封闭的泛型类型注册回退集合。

由于您RegisterAllOpenGeneric在方法中调用,因此您必须从单元测试RegisterSubscribers中删除调用。RegisterAllOpenGeneric当你这样做时,你会看到你的测试通过了。

于 2013-09-25T12:04:36.630 回答