Liskov替代和组成

假设我有这样一个类:

public sealed class Foo { public void Bar { // Do Bar Stuff } } 

我想扩展它以增加扩展方法可以做的事情….我唯一的选择是组合:

 public class SuperFoo { private Foo _internalFoo; public SuperFoo() { _internalFoo = new Foo(); } public void Bar() { _internalFoo.Bar(); } public void Baz() { // Do Baz Stuff } } 

虽然这有效,但它还有很多工作……但是我仍遇到一个问题:

  public void AcceptsAFoo(Foo a) 

我可以在这里传递Foo,但不是超级Foo,因为C#不知道SuperFoo确实符合Liskov替换意义……这意味着我的扩展类通过组合使用非常有限。

因此,解决它的唯一方法是希望原始的API设计者留下一个界面:

 public interface IFoo { public Bar(); } public sealed class Foo : IFoo { // etc } 

现在,我可以在SuperFoo上实现IFoo(因为SuperFoo已经实现了Foo,只需要更改签名)。

 public class SuperFoo : IFoo 

在完美的世界中,消耗Foo的方法将消耗IFoo:

 public void AcceptsAFoo(IFoo a) 

现在,由于通用接口,C#理解SuperFoo和Foo之间的关系,一切都很好。

最大的问题是.NET密封了许多偶尔会很好扩展的类,并且它们通常不会实现公共接口,因此采用Foo的API方法不会接受SuperFoo,也不能添加重载。

那么,对于那里的所有作曲迷们……你如何克服这个限制?

我唯一能想到的就是公开展示内部Foo,以便你偶尔可以传递它,但这看起来很混乱。

我担心简短的回答是,你不能没有做必要的事情,即改为组合实例变量。

可以允许隐式或显式转换为该类型(其实现只是传递组合实例),但这样,IMO会非常邪恶。

sixletter变量的答案是好的,我不会重复它,但如果你指出你希望你可以扩展哪些课程,我们可能会告诉你为什么他们阻止它。

在我开始研究自己的可重用库之前,我发现自己也在问同样的问题。 很多时候,如果不需要来自实现者的模糊或神秘的调用序列,您就无法扩展某些类。

在允许扩展类时,你必须要问:如果开发人员扩展了我的类,并将这个新类传递给我的库,我可以透明地使用这个新类吗? 我可以在这个新课上正常工作吗? 这个新课程真的会表现得一样吗?

我发现大多数情况下.Net Framework中的密封类具有您不了解的某些底层需求,并且鉴于当前实现无法安全地暴露给子类。

这并没有完全回答你的问题,但是它提供了关于为什么不能在.Net Framework中inheritance所有类的见解(以及为什么你应该接受密封你的一些类)。