为什么有人声称 C# 人无法进行面向对象编程?(相对于阶级导向)

StackOverflow https://stackoverflow.com/questions/52092

  •  09-06-2019
  •  | 
  •  

昨晚这引起了我的注意。

关于最新的 ALT.NET 播客 Scott Bellware 讨论了与 Ruby 不同,c#、java 等语言如何。不是真正的面向对象,而是选择“面向类”这个词。他们以非常模糊的术语谈论这种区别,而没有详细讨论或讨论太多的利弊。

这里真正的区别是什么?它有多重要?那么还有哪些语言是“面向对象”的呢?这听起来很有趣,但我不想学习 Ruby 只是为了知道如果我遗漏了什么。

更新: :在阅读了下面的一些答案后,人们似乎普遍同意参考的是鸭子类型。但我不确定我是否理解这最终会改变一切的说法。特别是如果您已经通过松散耦合进行了适当的 tdd 等等。有人可以给我举一个例子,说明我可以用 ruby​​ 做而用 C# 做不到的奇妙事情,并举​​例说明这种不同的 oop 方法吗?

有帮助吗?

解决方案

这里的鸭子打字评论更多地归因于 Ruby 和 Python 更 动态的 比 C# 。它实际上与面向对象的本质没有任何关系。

(我认为)Bellware 的意思是,在 Ruby 中,一切都是对象。哪怕是一个班。类定义是对象的实例。因此,您可以在运行时向其添加/更改/删除行为。

另一个很好的例子是 NULL 也是一个对象。在 Ruby 中,一切实际上都是对象。如此深入的面向对象在其整体中允许一些有趣的元编程技术,例如 method_missing。

其他提示

在面向对象的语言中,对象是通过定义对象而不是类来定义的,尽管类可以为给定抽象的特定的、千篇一律的定义提供一些有用的模板。在面向类的语言中,例如 C#,对象必须由类定义,并且这些模板通常在运行时之前被封装和打包并使其不可变。对象必须在运行时之前定义并且对象的定义是不可变的这种任意约束不是面向对象的概念;它是面向阶级的。

IMO,它确实过度定义了“面向对象”,但他们指的是 Ruby 与 C#、C++、Java 等不同,不使用 定义 一个类——你实际上只能直接使用对象。相反,例如在 C# 中,您可以定义 那么你必须 实例化 通过 new 关键字进入对象。关键是你必须 宣布 C# 中的类或描述它。此外,在 Ruby 中, 一切 ——例如,偶数——是一个对象。相比之下,C# 仍然保留了对象类型和值类型的概念。事实上,我认为这说明了他们对 C# 和其他类似语言的观点——对象 类型 和价值 类型 暗示一个 类型 系统,这意味着您拥有一整套系统 描述 类型而不是仅仅使用对象。

从概念上讲,我认为 OO 设计 是当今软件系统中用于处理复杂性的抽象。该语言是一种用于实现面向对象设计的工具——有些语言比其他语言更自然。我仍然认为,从更常见和更广泛的定义来看,C# 和其他语言仍然是 面向对象 语言。

OOP 的三大支柱

  1. 封装
  2. 遗产
  3. 多态性

如果一种语言可以完成这三件事,那么它就是 OOP 语言。

我非常确定 X 语言比 A 语言更适合 OOP 的争论将永远持续下去。

OO 有时被定义为 面向消息. 。这个想法是,方法调用(或属性访问)实际上是发送到另一个对象的消息。接收对象如何处理消息是完全封装的。通常,消息对应于随后执行的方法,但这只是实现细节。例如,您可以创建一个捕获所有处理程序,无论消息中的方法名称如何,都会执行该处理程序。

像 C# 中的静态 OO 没有这种封装。按摩一次 对应于现有的方法或属性,否则编译器会抱怨。然而,像 Smalltalk、Ruby 或 Python 这样的动态语言确实支持“基于消息的”OO。

因此,从这个意义上说,C# 和其他静态类型的 OO 语言并不是真正的 OO,因为它们缺乏“真正的”封装。

更新:这是新浪潮..这表明我们到目前为止所做的一切都已经过去了。似乎在播客和书籍中得到了很大的支持。也许这就是你听到的。

到目前为止,我们一直关心的是静态类,而不是 释放 面向对象开发的力量。我们一直在做“基于课堂的开发人员”。类是固定/静态模板以创建对象。类的所有对象都是平等创建的。

例如只是为了说明我一直在胡言乱语的内容......让我借用我刚刚有幸观看的 PragProg 截屏视频中的 Ruby 代码片段。“基于原型的开发”模糊了对象和类之间的界限。没有区别。

animal = Object.new                  # create a new instance of base Object

def animal.number_of_feet=(feet)     # adding new methods to an Object instance. What?
  @number_of_feet = feet
end
def animal.number_of_feet
  @number_of_feet
end

cat = animal.clone          #inherits 'number_of_feet' behavior from animal
cat.number_of_feet = 4

felix = cat.clone           #inherits state of '4' and behavior from cat
puts felix.number_of_feet   # outputs 4

这个想法是比传统的基于类的继承更强大的继承状态和行为的方式。它在某些“特殊”场景(我尚未理解)中为您提供了更大的灵活性和控制力。这允许诸如混合(重新使用没有类继承的行为)之类的事情。

通过挑战我们思考问题的基本原语,“真正的 OOP”在某种程度上就像“矩阵”......你继续WTF循环。像这个..其中 Container 的基类可以是 Array 也可以是 Hash,具体取决于生成的随机数位于 0.5 的哪一侧。

class Container < (rand < 0.5 ? Array : Hash)
end

Ruby、javascript 和新的团队似乎是这方面的先驱。我对这个还是一窍不通……阅读并尝试理解这一新现象。看来很强大啊。。太强大了..有用?我需要把我的眼睛再睁开一点。有趣的时光..这些。

我只听了引起你提问的播客的前 6-7 分钟。如果他们的意图是说 C# 不是一种纯粹的面向对象语言,那实际上是正确的。C# 中的所有内容都不是对象(至少基元不是,尽管装箱会创建包含相同值的对象)。在 Ruby 中,一切都是对象。Daren 和 Ben 在他们关于“鸭子打字”的讨论中似乎已经涵盖了所有基础,所以我不再重复。

这种差异(一切都是对象与一切非对象)是否重要/重要是一个我无法轻易回答的问题,因为我对 Ruby 的了解不够深入,无法将其与 C# 进行比较。在座的那些了解 Smalltalk 的人(我不知道,尽管我希望我了解)可能一直以有趣的方式看待 Ruby 运动,因为 Ruby 是 30 年前第一个纯 OO 语言。

也许他们在暗示鸭子类型和类层次结构之间的区别?

如果它走路像鸭子,叫起来像鸭子,就假装它是鸭子,然后踢它。

在C#、Java等中编译器对此大惊小怪:您可以对该对象执行此操作吗?

面向对象 vs 面向对象因此,面向类可能意味着:该语言是否担心对象或类?

例如:在Python中,要实现可迭代对象,只需要提供一个方法 __iter__() 返回一个对象,该对象具有名为的方法 next(). 。这里的所有都是它的:没有接口实现(没有这样的东西)。没有子类化。只是像鸭子/迭代器一样说话。

编辑: 当我重写所有内容时,这篇文章得到了赞成。抱歉,再也不会这样做了。原始内容包括建议学习尽可能多的语言,并且不用担心语言医生对语言的看法/说法。

那确实是一个抽象播客!
但我明白他们的意思——他们只是被 Ruby Sparkle 迷住了。Ruby 可以让您完成基于 C 和 Java 的程序员甚至想不到的事情 + 这些事情的组合可以让您实现意想不到的可能性。向内置 String 类添加新方法,因为您喜欢这样做,传递未命名的代码块供其他人执行,混合......传统的人们不习惯对象与类模板的变化太远。这肯定是一个全新的世界。

至于那些 C# 人还不够 OO……别放在心上..就当你对言语感到惊讶时所说的话吧。Ruby 对大多数人都是这样做的。
如果我必须推荐一种语言供人们在当前十年内学习..那就是鲁比。我很高兴我做到了..尽管有些人可能声称Python。但这就像我的看法..男人!:D

我不认为这专门针对鸭子打字。例如,C# 已经支持有限的鸭子类型 - 一个例子是您可以在 任何 实现 MoveNext 和 Current 的类。

鸭子类型的概念与 Java 和 C# 等静态类型语言兼容,它基本上是反射的扩展。

这确实是静态类型与动态类型的情况。只要有这样的事情,两者都是正确的面向对象。在学术界之外,这确实不值得争论。

垃圾代码可以用任何一种语言编写。优秀的代码可以用任何一种语言编写。绝对没有任何功能是一种模型能做到而另一种模型却做不到的。

真正的区别在于所完成的编码的性质。静态类型减少了自由度,但优点是每个人都知道他们在处理什么。即时更改实例的机会非常强大,但代价是很难知道您正在处理什么。

例如,对于 Java 或 C# 智能感知很简单 - IDE 可以快速生成可能性的下拉列表。对于 Javascript 或 Ruby,这变得更加困难。

对于某些事情,例如生成一个可供其他人编码的 API,静态类型确实具有优势。对于其他人来说,例如快速生产原型,优势在于动态。

了解你的技能工具箱中的这两种技能是值得的,但远没有理解你已经真正深入使用的技能工具箱那么重要。

面向对象是一个概念。这个概念是基于某些想法。这些想法的技术名称(实际上是随着时间的推移而演变的原则,从第一个小时起就没有了)已经在上面给出了,我不打算重复它们。我宁愿尽可能简单且非技术性地解释这一点。

OO编程的思想是存在对象。对象是小的独立实体。这些实体可能嵌入了信息,也可能没有。如果他们拥有此类信息,则只有实体本身可以访问或更改它。实体通过在彼此之间发送消息来相互通信。将此与人类进行比较。人类是独立的实体,大脑中存储有内部数据,并通过通信(例如,互相交谈)。如果你需要从别人大脑中获取知识,你无法直接访问它,你必须问他一个问题,他可能会回答你,告诉你你想知道什么。

基本上就是这样。这是面向对象编程背后的真正想法。编写这些实体,定义它们之间的通信,并使它们一起交互以形成应用程序。这个概念不受任何语言的束缚。这只是一个概念,如果您用 C#、Java 或 Ruby 编写代码,那并不重要。通过一些额外的工作,这个概念甚至可以用纯 C 来完成,尽管它是一种函数式语言,但它提供了该概念所需的一切。

现在不同的语言都采用了面向对象编程的概念,当然这些概念并不总是相同的。例如,某些语言允许其他语言禁止的内容。现在涉及到的概念之一是类的概念。有些语言有类,有些则没有。类是对象外观的蓝图。它定义了对象的内部数据存储,它定义了对象可以理解的消息以及是否存在继承(即 不是强制性的 对于面向对象编程!),类还定义了该类从哪个其他类(如果允许多重继承则为其他类)继承(以及如果存在选择性继承则定义哪些属性)。创建这样的蓝图后,您现在可以根据该蓝图生成无限数量的对象。

不过,有些面向对象的语言没有类。那么对象是如何构建的呢?嗯,通常是动态的。例如。您可以创建一个新的空白对象,然后向其动态添加内部结构,例如实例变量或方法(消息)。或者,您可以复制一个已存在的对象及其所有属性,然后对其进行修改。或者可能将两个对象合并为一个新对象。与基于类的语言不同,这些语言非常动态,因为您可以在运行时以甚至开发人员在开始编写代码时都没有想到的方式动态生成对象。

通常这种动态是有代价的:语言越动态,浪费的内存 (RAM) 对象就越多,一切都会变得越慢,因为程序流也极其动态,如果编译器没有机会预测代码或数据流,则很难生成有效的代码。JIT 编译器可以在运行时优化其中的某些部分,一旦它们知道程序流程,但是由于这些语言是如此动态,程序流程可以随时更改,迫使 JIT 丢弃所有编译结果并重新编译相同的代码一遍又一遍地。

但这只是一个很小的实现细节——它与基本的面向对象原则无关。没有任何地方说对象需要是动态的或者在运行时必须是可变的。维基百科说得很好:

编程技术可能包括信息隐藏,数据抽象,封装,模块化,多态性和继承等功能。

http://en.wikipedia.org/wiki/Object-oriented_programming

他们 可能 或者他们 不得. 。这一切都不是强制性的。强制性只是对象的存在,并且它们必须有彼此交互的方式(否则,如果对象不能彼此交互,它们将毫无用处)。

你问:“有人能给我举一个例子,说明我可以用 ruby​​ 做而用 C# 做不到的奇妙事情吗?这体现了这种不同的 oop 方法?”

Active Record 是一个很好的例子,它是 Rails 中内置的 ORM。模型类是在运行时根据数据库模式动态构建的。

这实际上很可能归结于这些人看到其他人在 C# 和 Java 中所做的事情,而不是支持 OOP 的 C# 和 Java。大多数语言可以在不同的编程范例中使用。例如,您可以用c#和scheme编写过程代码,也可以用java进行函数式编程。更多的是关于你想要做什么以及语言支持什么。

我会尝试一下。

Python 和 Ruby 是鸭子类型的。要使用这些语言生成任何可维护的代码,您几乎必须使用测试驱动开发。因此,对于开发人员来说,无需创建庞大的支持框架即可轻松地将依赖项注入到其代码中,这一点非常重要。

成功的依赖注入取决于拥有一个相当好的对象模型。两者有点像同一枚硬币的两面。如果您真正了解如何使用 OOP,那么默认情况下您应该创建可以轻松注入依赖项的设计。

由于依赖注入在动态类型语言中更容易,因此 Ruby/Python 开发人员感觉他们的语言比其他静态类型语言更好地理解了 OO 的教训。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top