1

两个类,D1D2派生自一个抽象基类B。它们中的每一个都共享在其中声明的公共公共接口,B但它们中的每一个也可能有自己特定的公共接口(例如D2hasD2.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()
    {            
    }
}

我将派生对象混合在一个集合中(例如列表),因为有时我必须对集合中的所有对象调用通用(继承)方法,但有时我只想调用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()上调用它是没有意义的(包含什么实现)。界面和组合也无济于事。我觉得这是一种非常常见的情况,我想知道在这种情况下最好的做法是什么?如何避免向下转换但允许调用特定于派生类型的公共方法?D1D1.Bar()

4

2 回答 2

5

在我看来,考虑到您的描述,“检查和沮丧”实际上是恰当的

有时我只想在 D2 对象上调用 Bar() :

现在这是一个有点奇怪的要求,但如果它一个要求,我认为以一种直接的方式实现它而不是为在基类上没有意义的操作添加无操作实现是合理的。

但是,我会略有不同:

foreach (var d2 in list.OfType<D2>())
{
    d2.Bar();
}

现在这正是你的意思:)

于 2013-08-16T13:44:27.580 回答
1

似乎 D2 向 B 添加的行为比最初定义的要多。这表明违反了单一职责原则(D2 做的不止一件事)。悲观情绪还表明,将 D1 和 D2 放在同一个列表中并不一定有意义。那么,也许“IS A”关系在这里不合适?我会尝试切换到组合并为 D1 和 D2 使用一组精确定义的接口,参考Interface Segregation Principle。然后可能 D1 和 D2 可以混合在一个列表中,但前提是您对一个特定行为(接口)感兴趣,而对于另一个接口,您将只有 D2 在列表中(不知道它,也不关心它)。

于 2013-08-16T14:11:59.217 回答