里氏替换和复合

发布于 2024-07-13 08:01:05 字数 1285 浏览 10 评论 0原文

假设我有一个这样的类:

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,但不能传递 super Foo,因为 C# 不知道 SuperFoo 确实符合里氏替换的意义...这意味着我通过组合扩展的类非常有限使用。

所以,解决这个问题的唯一方法就是希望最初的 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,这样你就可以偶尔传递它,但这看起来很混乱。

Let say I have a class like this:

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

And I want to extend it to add something beyond what an extension method could do....My only option is composition:

public class SuperFoo
{
    private Foo _internalFoo;

    public SuperFoo()
    {
        _internalFoo = new Foo();        
    }

    public void Bar()
    {
        _internalFoo.Bar();
    }

    public void Baz()
    {
        // Do Baz Stuff
    }
}

While this works, it is a lot of work...however I still run into a problem:

  public void AcceptsAFoo(Foo a)

I can pass in a Foo here, but not a super Foo, because C# has no idea that SuperFoo truly does qualify in the Liskov Substitution sense...This means that my extended class via composition is of very limited use.

So, the only way to fix it is to hope that the original API designers left an interface laying around:

public interface IFoo
{
     public Bar();
}

public sealed class Foo : IFoo
{
     // etc
}

Now, I can implement IFoo on SuperFoo (Which since SuperFoo already implements Foo, is just a matter of changing the signature).

public class SuperFoo : IFoo

And in the perfect world, the methods that consume Foo would consume IFoo's:

public void AcceptsAFoo(IFoo a)

Now, C# understands the relationship between SuperFoo and Foo due to the common interface and all is well.

The big problem is that .NET seals lots of classes that would occasionally be nice to extend, and they don't usually implement a common interface, so API methods that take a Foo would not accept a SuperFoo and you can't add an overload.

So, for all the composition fans out there....How do you get around this limitation?

The only thing I can think of is to expose the internal Foo publicly, so that you can pass it on occasion, but that seems messy.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

顾冷 2024-07-20 08:01:05

我发现自己在问同样的问题,直到我开始研究自己的可重用库。 很多时候,您最终会得到某些类,如果不要求实现者执行模糊或神秘的调用序列,就无法扩展这些类。

当允许扩展您的类时,您必须问:如果开发人员扩展了我的类,并将这个新类传递到我的库,我可以透明地使用这个新类吗? 我能在这个新班级中正常工作吗? 这个新班级的行为真的会一样吗?

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

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

I found myself asking that same question until I started working on reusable libraries of my own. Many times you wind up with certain classes that just cannot be extended without requiring obscure or arcane sequences of calls from the implementor.

When allowing your class to be extended, you have to ask: if a developer extends my class, and passes this new class to my library, can I transparently work with this new class? Can I work properly with this new class? Is this new class really going to behave the same?

I've found that most of the time the sealed classes in the .Net Framework have certain under-the-hood requirements that you aren't aware of, and that given the current implementation cannot be safely exposed to subclasses.

This doesn't exactly answer your question, but it provides insight as to why not all classes are inheritable in the .Net Framework (and why you should probably entertain sealing some of your classes too).

瑶笙 2024-07-20 08:01:05

恐怕简短的答案是,您不能不执行所需的操作,即传递组合的实例变量。

可以允许隐式或显式转换为该类型(其实现只是传递组合实例),但在我看来,这将是相当邪恶的。

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

I'm afraid the short answer is, you can't without doing what is required, i.e. pass the composed instance variable instead.

You could allow an implicit or explicit cast to that type (whose implementation simply passed the composed instance) but this would, IMO be pretty evil.

sixlettervariable's answer is good and I won't rehash it but if you indicated which classes you wished you could extend we might be able to tell you why they prevented it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文