3

我有一个有趣的问题,我一直在盘旋,但我似乎从来没有安静地找到解决方案。

我倾向于成为一名防御性程序员,因此我尝试编写代码来防止问题发生,而不是在问题发生后立即做出反应。为此,我有以下情况。采取以下代码:

public class Base {}
public Interface IBase {}

public class Derived : Base, IBase {}
public class Derived2 : Base, IBase {}
...
public class DerivedN : Base, IBase {}

public class X : Base {}
public class Y : IBase {}

我需要将派生自 Base 并实现 IBase 的对象列表传递给集合,并且我需要确保只有同时具有这两者的对象才会添加到列表中。此外,可以有任意数量的类同时具有这两者,因此我不能将派生类用作约束。

如果我制作 Base 类型的列表,那么我可以添加一个 Y 对象。如果我将其设为 IBase 类型,则可以添加 X 类型的对象(两者均不允许)。

当然,我可以创建自己的通用集合类,它具有两种类型并且对两者都有约束。但是,我不想对所有可能的集合类型都这样做,并且复制所有这些功能需要付出很多努力(即使您只是将方法调用转发到包含的类)。

我还可以创建一个从 Base 和 IBase 派生的 BaseWithIBase 类,并将其用作我的集合类型,但如果我不需要,我真的不想强制另一个抽象。

我不希望这是运行时检查,因此不接受遍历树并抛出异常。

任何人都可以提出更好的方法来解决这个问题吗?

注意:Base 和 IBase 没有关系,只是指出它们都是不同类型的基础项。

编辑:

似乎每个人都想坚持“你不需要这样做”并且它“不是 OOP”。没有东西会离事实很远。我试图从问题中删除特定内容以防止此类问题和评论,因此我将包括我的真实情况。

该代码是基于 .NET Frameworks ServiceProcess.ServiceBase 类的 Windows 服务框架的实现。我在此基础上添加了我自己的框架,该框架旨在高度依赖注入,并且高度可测试。

该集合必须包含派生自 ServiceBase 和 IService 的对象。IService 是我的框架扩展,用于我的代码和测试。基本上就是这样:

public interface IService 
{
    void Start();
    void Stop();
}

此外,我还有一些其他的接口:

public interface IRestartableService
{
    void Restart();
}

public interface IConfigurableService
{
    void Configure();
}

等等..等等..服务可能看起来像这样:

public class MyService : ServiceBase, IService, IConfigurableService {}

我的代码需要 IService,Windows 需要 ServiceBase,因此两者都需要,因为我使用 IService,而 Windows 使用 ServiceBase。我只需要IService,其他接口是可选的。

4

4 回答 4

8

您可以简单地创建自己的包装器集合:

// TODO: Work out which collection interfaces you want to implement
public class BaseList
{
    // Or use List<IBase>, if that's how you'll be using it more often.
    private List<Base> list = new List<Base>();

    public void Add<T>(T item) where T : Base, IBase
    {
        list.Add(item);
    }
}

通过使用具有这两个约束的泛型方法,您可以确保Add只能使用适当的类型参数调用该方法。

您可以有两种方法将数据公开为IEnumerable<T>- 一个返回IEnumerable<IBase>(使用Cast<T>)和一个返回......这将允许您在IEnumerable<Base>一类型上使用 LINQ ,但当然不能同时使用两者。

但是,我怀疑您可能会在其他地方发现这很尴尬-您可能会发现自己在代码中乱扔了通常不需要的通用方法。虽然可能有充分的理由同时需要类部分和接口部分,但值得退后一步考虑它们是否真的都是必要的。例如,您是否可以向界面添加一些额外的东西,以便您可以取消类约束?

于 2013-06-11T05:45:44.827 回答
0

您的问题没有很好的答案,因为设计本身并不真正适合在 C#/.NET 中实现的 OOP。

如果您绝对需要一个集合,其中每个元素静态提供两个独立的接口,那么包装器集合或某些包装器类Wrapper<TFirst, TSecond, T> : IBoth<TFirst, TSecond>都可以解决您的问题。

例子:

public interface IBoth<TFirst, TSecond> {
    TFirst AsFirst();
    TSecond AsSecond();
}

public class Wrapper<T, TFirst, TSecond> : IBoth<TFirst, TSecond>
   where T : TFirst, TSecond
{
    private readonly T _value;

    public Wrapper(T value) {
        _value = value;
    }

    public TFirst AsFirst() {
        return _value;
    }

    public TSecond AsSecond() {
        return _value;
    }
}

然而,真正的问题是你为什么需要它。并不是说标准的 OOP 模型是完美的,但是如果审查原始设计决策,通常可以更容易地解决问题。

于 2013-06-11T05:55:26.280 回答
0

ServiceBase另一种选择是在大多数代码中完全忽略并创建一个ServiceBaseAdapter用于与界面不友好的代码进行通信的代码。这样的适配器可以在调用它的方法时调用你的接口方法。

于 2013-06-11T23:07:29.950 回答
-2

尝试这样的事情:

List<object> collection = new List<object>();
foreach(var obj in collection.OfType<Base>().OfType<IBase>())
{
// Do what ever you want
}
于 2013-06-11T05:51:15.927 回答