.NET 中的继承没用?
Posted
技术标签:
【中文标题】.NET 中的继承没用?【英文标题】:Inheritance in .NET is useless? 【发布时间】:2011-06-21 22:35:33 【问题描述】:我对 .NET 编程很陌生。我知道 .NET 编程是 100% 面向对象的。我在一本关于 ASP.NET 4 的书中读到的一段有趣的段落指出
继承没有你想象的那么有用。在普通应用程序中,大多数类使用 包含和其他关系而不是继承,因为继承会使生活复杂化 不必要地没有带来很多好处。著名的 .NET 程序员 Dan Appleman 曾经 将继承描述为“您几乎永远不会使用的最酷的功能。”
我在这里有点困惑,我需要 .NET 程序员告诉我应该拿什么,应该留下什么。
编辑
请大家理解我的问题,首先作者并没有按字面意思说“.NET 中的继承是无用的”,这是我用一些词压缩漏洞问题以适合标题的方式。其次,这本书来自 Apress,书名是:《Beginning ASP.NET 4 in C# 2010》第 72 页。
【问题讨论】:
没用????对基类库的设计者说 哪本书?我不想买。 您想感受使用仅支持通过包含来构建对象的语言的痛苦吗?尝试使用VB6 您读过的一本奇怪的书 :-) .NET 中到处都在使用继承。 CLR 中的唯一限制是一个类只能从一个基类(和许多接口)继承。有些语言“模拟”多继承(例如 .NET 的 eiffel)。 程序员初学者需要继承,就像每天上班需要一辆法拉利一样。毫无意义,除非您拥有将所有交通信号灯变为绿色的小工具。位于熟练程序员的耳朵之间。 【参考方案1】:我认为在大多数介绍性的面向对象的学术课程中所呈现的继承 是毫无用处的。您将始终看到 Animal 或 Shape 的层次结构,但大多数实际问题并非如此。
这两个例子很快就被打破了。看看林奈生物学中的物种分类或this article 关于矩形形状,看看我的意思。任何继承层次结构都会在一定数量的级别之后分解,因为叶类离使用基类维护 IS-A 越来越远。
任何模型都代表了对保留内容和排除内容的选择。继承当然适用于某些模型,但对其他模型则不太适用。
继承运作良好的示例是数据结构和接口。接口表示方法签名是继承的,而忽略了实现细节。因此,如果您可以抽象出所有 List 或 Set 或 Map 实现的共同点,那么您就有了一些提供结构但将实现细节留给设计人员的东西。
我会说继承在这些情况下工作得很好。
与大多数笼统的陈述一样,您引用的陈述并非普遍正确。
【讨论】:
+1 这一点我自己说得再好不过了,所以现在我什至不必尝试了。这是一个极好的答案,尤其是最后一句话:“就像大多数笼统的陈述一样,您引用的陈述并非普遍正确。” +1;我只想给en.wikipedia.org/wiki/Liskov_substitution_principle加点帽子 虽然继承这一术语经常被应用于接口以及类(或类型),但我相信这会造成混淆和误解。接口不是继承。一个类(或类型)从另一个类(或类型)的继承定义了实现用法,即在派生类型的实例中调用函数应该在父类中执行代码。指定一个类实现一个接口不是继承,它只是一个要求类包含接口定义的函数、属性和方法的约束。 我没有看到任何人感到困惑的证据。事实发生近六年后,该评论似乎没有必要。【参考方案2】:继承是一个 OO 概念,在 .NET 中与任何其他 OO 语言相比变化很小,除了 .NET 只允许单一继承(对于类);这也很常见。
作者说得对,一般继承很容易误用,一般封装是更好的选择,但这绝不是.NET特有的。
很多时候使用继承是非常有意义的,你不应该回避它;多态性等就是一个典型的例子。但是大多数标准的“数据输入数据输出”代码不需要多态性。
它就在那里,很有价值——不,必不可少(至少,为了保持理智;如果你喜欢以艰难的方式做事,你当然可以避免它)。随意忽略它或使用它。
但请自行决定。我个人认为作者是在混淆,也许有点短视。
【讨论】:
【参考方案3】:哎哟。继承不仅有用,它也是一个核心概念,因为每个类都继承自 System.Object(不确定是否有没有的 hacks/CLR 东西)。
此外,在 ASP.net MVC 等某些技术中,您几乎肯定会使用继承(通过创建基本控制器)。
每个人都一直在使用继承,大多数时候并没有真正意识到这一点。在大多数中小型应用程序中没有那么多继承,而是经常使用接口,例如依赖注入。然而,几乎每个非平凡的应用程序都倾向于在某个地方浮动一个基类,其他类从该基类继承。
我猜这就是作者的意思,定义基类或接口之间的决定。微软推荐基类,但在现实生活中接口似乎更常见。此外,由于 C# 不支持多重继承(具有多个基类的类)接口提供了更大的灵活性,但代价是无法集中公共代码或强制执行某些构造函数。
【讨论】:
好点。我要补充一点,您不需要在基类和接口之间进行选择。你可以同时使用它们。使用接口来定义你的抽象;使用实现接口的基类来集中公共代码。我的团队使用这种方法,非常有益。 组合不是关于实现接口。例如,组合是将某个类的一个对象作为另一个类的成员。 en.wikipedia.org/wiki/Object_composition @Edgar 好点,当我写这篇文章时,我还没有读过设计模式这本书,它确实将组合描述为与我不同的东西。更新了答案。 "每个人都一直在使用继承,大多数时候并没有真正意识到这一点。" - 这正是重点。 “在一个普通的应用程序中......继承会使生活复杂化......”仅仅因为你使用它(在一个由 1% 的程序员设计和实现的框架中)并不意味着你必须深入理解它——否则,我们' d 建议初学者学习 Win32 API、x86 ASM 等。【参考方案4】:鉴于您承认对主题不熟悉,而且您正在阅读的书已经出版,您似乎很可能只是误解了预期的信息。或许那段文字写得不是很清楚。继承非常有用并且被广泛使用。
【讨论】:
不要那么肯定;我看过一些非常可怕的书。我什至把我的名字(作为技术审查员)从一个人中删除,因为他们拒绝进行必要的更改(即修复对类型系统和语言理解的根本缺陷)。【参考方案5】:我认为这里需要说的大部分内容已经被比我更有经验的人说了。但是,作为从 VB6/VBA 迁移到 .net 中可用的真正继承的无尽美妙世界的人,我可以说阻止新手将其视为最终解决方案不一定是坏事。
当我第一次开始探索这个想法时,我发现通过继承可以做各种很酷的事情。但最后,我得到了一些非常糟糕的代码、难以维护的类层次结构和整体混乱。对于新手来说,这似乎是一个几乎神奇的解决方案(对于那些首先忍受 VB6/VBA 痛苦多年的人来说更是如此)。现实情况是,就像设计模式一样,继承经常被用来“因为我可以”而不是“因为我需要”。
我敢打赌,作者的意图是传达这样一个观点,即继承,通常被视为经验较少的人的“一阶”解决方案,只有在它是正确的解决方案时才应该真正使用。并且很容易认为这可能是在错误情况下的适当解决方案。
使用继承作为一种使编码工作更容易的方法可能很诱人。在我早期学习 vb.net 时,我当然遇到了一些困难,而且通常,从一个难以创建的类继承似乎是一个有吸引力的解决方案,可以解决创建不同风格的难代码的问题。当然,对于正在学习的人来说,今天的困难代码就是明天的首选代码。
继承是 OOP 中的核心概念。但它很容易被滥用和/或应用不当。仔细设计类层次结构对于正确使用继承概念至关重要,而且在某些情况下,包含和委托是完成工作的首选方式。
但是说继承毫无用处显然是荒谬的。
要检查接口/基类/继承建模的好方法,请查看 .net 框架本身。从对象资源管理器中可以检查继承模型,因为它应用于我们每天使用的核心组件。
【讨论】:
感谢 XIVSolutions,另一个启发性的答案。 在“因为我可以”和“因为我需要”之间的选择中,我更喜欢第三种选择:“因为我应该” @Phoog - 用“因为我应该”替换“因为我需要”。在我的上下文中,我想我的意思是一样的。有些地方继承是适当的解决方案,有些地方则不是。虽然我承认可能是介于两者之间的一些灰色区域,但我正在解决这样一个想法,即继承通常是看起来更容易做某事的方式,这并不总是最好或最合适的方式。在我看来,“因为我可以”类似于使用继承,只是因为它“整洁”,所以我想使用它。这对新人来说是一个很大的诱惑。我知道,因为我做到了! 我也这样做了,我全心全意支持你的立场。我只是想指出,继承可以在可能不被视为“需要”的情况下简化代码。但“需要”对不同的人可能意味着不同的东西!【参考方案6】:就像一个很小的例子,如果没有继承,当你在 windows 中创建一个表单,或者在 web 应用程序中创建一个 ASP.Net WebPage 时,你必须为所有“Window”添加所有代码功能(例如下面**),并将此代码添加到您创建的每个此类表单或网页中... 没用???几乎没有!
您从 System.Windows.Form“继承”的某些功能示例 (移动窗口,调整大小,最小化,最大化,恢复它,在它下面和附近绘制屏幕,处理鼠标点击它产生的消息,控制它是否有焦点,在它和它之间来回传递数据其他屏幕元素等)
【讨论】:
这是向范式新手解释它的好方法。我们其他人可能会有点陷入这种明显的荒谬主张,而忘记了提问者的经验水平。为此 +1。【参考方案7】:随着我编写的代码越来越多,我得出了类似的结论(尽管没有那么挑衅)。一个原因是缺乏多重继承(我现在同意这是一件好事),另一个是依赖关系失控。
这些天来,我试图避开继承和抽象类,而更多地依赖组合和接口(特别是现在扩展方法可以弥合差距,为接口提供基本方法实现)。
这导致代码更易于重用和测试,并且更容易进行依赖管理(使用依赖倒置)。
【讨论】:
【参考方案8】:在一般的商业案例中,不,您不会创建深度的类型层次结构。但是,您将一直直接使用继承。
总是。
一个 ASP.NET .aspx 页面,其中包含来自您的代码隐藏类的代码隐藏继承。您的代码隐藏类本身继承自 Page 类,并且您覆盖其上的方法。如果你不理解 override 和 virtual(都是与继承相关的概念)的含义,那么你就是在卖空自己。
整个框架中有数千个示例,其中对继承和相关概念的理解将帮助您组织对日常使用的对象的理解。一些真正深入研究的最佳方法与 LINQ 和泛型(IEnumerable、IQueryable、ICollection 等)、MVC/Web 表单和实体框架以及您在哪里创建与您要解决的业务问题相关的 POCO 对象。
简短的回答是你需要理解它,即使你不是在写一个框架。书中的段落 (excerpt from page 72) 的直率和误导性有点令人尴尬。
【讨论】:
深思熟虑 - 你的几个例子都说明了这一点:IEnumerable、IQueryable、ICollection 等(这不完全是“继承”)。 @Mark - 是的。我在文本中添加了“继承和相关概念”。 使用一个继承设计的框架和理解它的使用是完全不同的,IMO。 ASPX 文件从代码隐藏继承的事实在 99.9% 的情况下都无关紧要。要真正利用这些知识做任何事情,还需要对 ASP.NET 编译和代码生成有相当深入的了解——这对于初学者的文本来说并不完全是素材。从 Page 继承也是一个无关紧要的细节 - 您可能重写的方法没有基类实现,并且确实更适合将其视为事件处理程序。【参考方案9】:抛开 SO'ers 的反对似乎反应过度,这是当今面向对象程序员中相当普遍的知识。
我略微不同意引文的精确用词,但这种情绪没有什么比"prefer composition over inheritance"更具争议性了。
专门针对a book called "Beginning ASP.NET",我认为这是相当不错的建议。作为 ASP.NET 中的一种设计机制,您需要深入处理继承的次数非常有限——尤其是作为初学者。
【讨论】:
【参考方案10】:已经有很多很好的答案,但是我在自己学习 OOP 时遇到了困难,所以这可能会有所帮助。
首先看看this article,它展示了如何在普通的asp.net 应用程序中使用类,然后继承。
请参阅this link 了解真实世界的继承示例。它是Web Control class in asp.net。它是一个包含许多控件的通用内容的类,向下滚动并查看继承层次结构并查看有多少控件继承来自该控件。
【讨论】:
我不认为继承没用,我的问题很清楚。 对不起,我只是想给你一个真实世界的例子,我确实说过 "if" 。我编辑了问题。以上是关于.NET 中的继承没用?的主要内容,如果未能解决你的问题,请参考以下文章
Protobuf-net 中的继承:ProtoInclude 和兼容性
无法在 ADO .NET 实体模型 edmx 中的类继承中执行覆盖