C# · 12月 30, 2021

c# – 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变得非常恶劣.

sixlettervariable的答案是好的,我不会rehash它,但如果你指出你希望你可以延伸的课程,我们可能可以告诉你为什么他们阻止它.