两个类D1
和D2
派生自抽象基类B
。它们中的每一个都共享在B
中声明的公共公共接口,但每个接口都可能有自己的特定公共接口(例如D2
具有D2.Bar()
,这仅对D2
个对象有意义):
public abstract class B
{
public int N { get; set; }
public abstract void Foo();
}
public class D1 : B
{
public override void Foo()
{
}
}
public class D2 : B
{
public override void Foo()
{
}
public void Bar()
{
}
}
我将派生对象混合在一个集合中(例如列表),因为有时我必须在集合中的所有对象上调用常用(继承)方法,但有时我想在{{1}上调用Bar()
仅对象:
D2
我觉得代码闻到了这里。向下倾斜是一个坏主意,根据类型检查做出决定是个坏主意。如果我将 var list = new List<B>();
list.Add(new D1());
list.Add(new D2());
foreach(var b in list)
if(b is D2)
(b as D2).Bar();
移到基类,那么在Bar()
个对象上调用它是没有意义的( D1
的实现包含什么?)。界面和组合也没有帮助。我觉得这是一种非常普遍的情况,我想知道这种情况下的最佳做法是什么?如何避免向下转换但允许调用特定于派生类型的公共方法?
答案 0 :(得分:5)
在我看来,实际上,根据您的描述,“检查和投降”是正好:
有时我想只在D2对象上调用Bar():
现在这有点奇怪的要求,但如果 是一个要求,我认为以一种简单的方式实现它是合理的,而不是为没有的操作添加no-op实现在基类上有意义。
但是,我会稍微改变一下:
foreach (var d2 in list.OfType<D2>())
{
d2.Bar();
}
现在这说明了你的意思:)
答案 1 :(得分:1)
似乎D2正在向B添加比最初定义的更多行为。这表明Single Responsibility Principle正在被侵犯(D2不止一件事)。垂头丧气也表明将D1和D2保持在同一个列表中并不一定有意义。那么,也许“IS A”关系在这里不合适?我尝试切换到合成并使用一组精确定义的D1和D2接口,参考Interface Segregation Principle。那么D1和D2可能会在一个列表中混合,但只有当你对一个特定的行为(接口)感兴趣时,对于另一个接口你才会在列表中只有D2(不知道它,也不关心它)。 / p>