循环对象的接口

问题描述 投票:0回答:2

我正在创建一个通用服务定位器,但遇到了问题。 我有一种添加或替换服务的方法。如果当前服务列表中只有 1 个候选者,则必须更换该服务。

// IA  - TestA
// IB  - TestB
// IA/IB - TestAB
// <IB> TestAB 
public static void AddOrReplaceService<T>(T serviceToAdd) where T:  class
{
    if (serviceToAdd == null)
        throw new ArgumentNullException(nameof(serviceToAdd));
    List<T> foundServices = new List<T>();
    int index = -1;
    for (int i = 0; i < _services.Count; i++)
    {
        if (!(_services[i] is T) 
            && !_services[i].GetType().IsAssignableFrom(typeof(T))
            && !_services[i].GetType().IsInstanceOfType(serviceToAdd)
            && !serviceToAdd.GetType().IsInstanceOfType(_services[i]))
            continue;
        foundServices.Add((T) _services[i]);
        if (index == -1)
            index = i;
    }
    switch (foundServices.Count)
    {
        case 0:
            AddService(serviceToAdd);
            break;
        case 1:
            _services[index] = serviceToAdd;
            return;
        default:
            throw new MultipleServicesAlreadyExistsException("Multiple services found with " + typeof(T).Name
                                                + foundServices.ToStringCollection(", "));
    }
}

为了测试我的服务定位器,我有 2 个接口

IA
IB
和 3 个类,
TestA : IA
TestB : IB
TestAB : IA, IB

问题是,如果

TestA
TestB
都在列表中,并且您尝试添加
TestAB
,它应该给出例外,因为
TestA
TestB
都在实现
TestAB
的接口。

我尝试添加一堆

AssignableFrom
等逻辑。但是,我无法让它工作。

这是单元测试的一部分

[Test]
public void Test()
{
    try
    {
        ServiceLocator.Reset();
        Assert.IsTrue(AddService<IA>(new TestA(), false));
        Assert.IsTrue(AddService<IB>(new TestB(), false));
        // TestAB implements IA and IB so replacing should fail;
        Assert.IsTrue(AddOrReplaceService<IB>(new TestAB(), true));

非常感谢您的帮助!预先感谢。

c# generics design-patterns reflection
2个回答
0
投票

使用以接口类型为键的字典会更容易且性能更高,因为不需要循环。

private static readonly Dictionary<Type, object> _services = new Dictionary<Type, object>();

public static void AddOrReplaceService<T>(T serviceToAdd) where T:  class
{
    if (serviceToAdd == null)
        throw new ArgumentNullException(nameof(serviceToAdd));
    _services[typeof(T)] = serviceToAdd;
}

这会自动执行几件事:

  1. 它确保每个接口仅注册一个服务。
  2. 如果接口尚未注册,它会自动添加服务。
  3. 它会自动替换同一接口的现有服务。

由于您将参数

serviceToAdd
输入为
T
,C# 编译器确保服务类与接口
T
的分配兼容(无需测试)。此外,无需测试您要替换的服务是否是正确的类型,因为它是通过相同的方法添加的,只能使用正确类型的服务进行调用。

参见:Dictionary.Item[TKey] 属性


0
投票

嗯,你的逻辑有问题。这是发生的事情:

  1. 2 在 UT 中首次调用 AddOrReplaceService() 将 2 个服务添加到服务集合中
  2. UT 中的第三次调用将用 TestAB 的副本替换 TestB 类,因为 TestA 不可从 IB 分配

永远不会出现

foundServices
的数量比 1 多的情况。 因此,为了看到抛出异常,您需要从
AddOrReplaceService()
内的开关中删除第二个条件。除了您尝试添加到集合中的接口之外,您不应该检查任何接口的实现。

© www.soinside.com 2019 - 2024. All rights reserved.