9

假设我有这样的课程:

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 公开,这样你可以偶尔通过它,但这似乎很乱。

4

2 回答 2

14

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

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

我发现大多数时候 .Net 框架中的密封类具有某些您不知道的底层要求,并且鉴于当前的实现不能安全地暴露给子类。

这并不能完全回答您的问题,但它提供了关于为什么并非所有类都可以在 .Net 框架中继承的见解(以及为什么您可能也应该考虑密封您的一些类)。

于 2009-02-16T18:56:28.983 回答
4

恐怕简短的回答是,你不能不做需要做的事情,即传递组合的实例变量。

可以允许对该类型进行隐式或显式强制转换(其实现只是传递了组合实例),但这将是 IMO 非常邪恶。

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

于 2009-02-16T19:19:05.793 回答