.NET 中的一切都是对象吗?

Posted

技术标签:

【中文标题】.NET 中的一切都是对象吗?【英文标题】:Is everything in .NET an object? 【发布时间】:2010-09-30 23:24:44 【问题描述】:

请帮助我们解决“几乎”一切都是对象的争议 (an answer to Stack Overflow question As a novice, is there anything I should beware of before learning C#?)。我认为情况就是这样,因为 Visual Studio 中的所有内容至少都以结构的形式出现。请发布参考,以免它成为“现代蠢货”(This American Life)。

请注意,这个问题指的是 C#,不一定是 .NET,以及它如何处理后台数据(显然都是 1 和 0)。

这里是“一切都是对象”的cmets:

呃,不,不是。 – 二元忧 我想要一个例子... – scotty2012 并非一切都源自 基本类型对象? – 细雨 大多数事物都是对象... – Omar Kooheji 值类型、整数、双精度、对象 引用(不是他们的对象 自我)等不是对象。他们能 被“装箱”以看起来像对象(例如 i.ToString()) 但实际上他们是 原始类型。将条目更改为 “几乎一切都是对象”和 我将删除反对票 – 二进制 担心 感谢您的澄清。一世 想想你能做到的最低水平 与 C# 中的 int 交互是 作为一个结构,它不是一个对象? - http://msdn.microsoft.com/en-us/library/ms173109.aspx – 细雨 Int32 不继承自 ValueType 哪个继承自对象?如果是这样的话, 尽管有这种行为,但 int 是 目的。 ——克里斯·法默 不,int 的盒装类型继承 来自 ValueType,它继承自 目的。它们不是对象 传统意义上,因为 a) 一个 int 不是对 int 的引用,它是 诠释。 b) 整数不是垃圾 集。如果你声明一个 Int32, 那么 int 是 4 个字节 堆栈,故事的结尾——二元烦恼

对象的定义:“Object”作为 System.Object 类的继承者,“object”作为类型的实例,“object”作为引用类型。”

【问题讨论】:

@annakata:除非有明确的答案。圣战没有​​明确的答案。 呲牙咧嘴!我正要发布这个问题,看看社区怎么说。附:在选择答案之前,我会等着看 Jon Skeet 或 Marc Gravell 说了什么。 大声笑 - 在辩论中一方是对的,另一方是错的,在圣战中则相反 在继续讨论之前,需要澄清一下“对象”的预期定义。 @Binary: Object 类派生自 jon skeet? :) 【参考方案1】:

这里的问题是,这实际上是两个问题——一个是关于继承的问题,在这种情况下答案是“几乎所有东西”,另一个是关于引用类型与值类型/内存/装箱的问题,这种情况下的答案是“不”。

继承:

在 C# 中,以下是正确的:

所有值类型,包括枚举和可为空的类型,都派生自System.Object。 所有类、数组和委托类型都派生自System.Object。 接口类型不是从System.Object 派生的。它们都可以转换为System.Object,但接口只能派生自其他接口类型,System.Object 不是接口类型。 没有指针类型派生自System.Object,也没有任何指针类型可以直接转换为System.Object。 “Open”类型参数类型也不是从System.Object 派生的。类型参数类型不是从任何东西派生的;类型参数被限制为从有效的基类派生,但它们本身不是从任何东西“派生”的。

来自the MSDN entry for System.Object:

支持 .NET 中的所有类 框架类层次结构并提供 派生类的低级服务。 这是所有的终极基类 .NET 框架中的类;它是 类型层次结构的根。

语言通常不需要 声明继承的类 对象,因为继承是 隐含的。

因为 .NET 中的所有类 框架来源于Object, 对象中定义的每个方法 类在所有对象中都可用 系统。派生类可以而且可以 覆盖其中一些方法。

因此,并非 C# 中的每种类型都派生自 System.Object。即使对于那些类型,您仍然需要注意reference types 和value types 之间的区别,因为它们的处理方式非常不同。

拳击:

虽然值类型从System.Object继承,但它们在内存中的处理方式与引用类型不同,并且它们在代码中通过方法传递的语义也不同。实际上,值类型不会被视为对象(引用类型),除非您通过将其装箱为引用类型来明确指示您的应用程序这样做。见more information about boxing in C# here。

【讨论】:

因为他是对的。继承链为:Object -> ValueType -> Int32。这意味着 Int32 是一个对象,但也是一个值类型。我认为 struct 是 ValueType 的简写。 来自 MSDN:“装箱和拆箱使值类型能够被视为对象”(msdn.microsoft.com/en-us/library/yz2be5wk(VS.80).aspx)。因此,这意味着值类型 ARENT 对象,如果它们必须被装箱才能被“视为对象”。 该文档显然具有误导性,因为这是对 OOP 定义的错误解释,其中 object 只是类型的实例。文档的这一部分应解释为“使值类型成为引用类型受到威胁”。他们用一句话来反驳。 什么是对象? :) 我认为这就像数学中的一个集合。什么是“一切”? 我更正了关于继承的部分。以下 C# 类型不派生自 System.Object:接口、指针、类型参数。【参考方案2】:

聚会有点晚了,但我在 SO 上的搜索结果中发现了这一点,并认为下面的链接会对后代有所帮助:

Eric Lippert discusses this very thoroughly,有一个更好(合格)的声明:

纠正这个错误的方法是简单地将“派生自”替换为“可转换为”,并忽略指针类型:C# 中的每个非指针类型都可以转换为对象。

如果您讨厌从编写编程语言的人那里阅读详细说明的解释,那么它的要点是(抛开指针),接口或泛型参数类型声明(“T”)之类的东西不是对象,而是保证在运行时被视为对象,因为它们有一个明确的实例,这将是一个对象。其他类型(类型、枚举、委托、类等)都是对象。包括值类型,正如其他答案所讨论的那样,可以将其装箱为对象。

【讨论】:

【参考方案3】:

这里有些人对面向对象编程中的“对象”有一个奇怪的概念。为了使某物成为对象,它确实必须是引用类型,或者更一般地说,遵循任何正式的实现。

这意味着您可以在面向对象的世界中作为一等公民对其进行操作。由于您可以对 C# 中的值执行此操作(感谢自动装箱),所以一切确实是一个对象。在某种程度上,这甚至适用于函数(但可以说不适用于类)。

这在实践中是否相关是另一个问题,但这是我再次注意到的 OOP 的普遍问题。没有人对 OOP 的定义很清楚(是的,大多数人都同意它与多态性、继承和封装有关,有些人为了更好地衡量了“抽象”)。

从使用的角度来看,C# 中的每个值都像对象一样处理。也就是说,我喜欢当前接受的答案。它提供了两个重要的技术方面。

请注意,在其他情况下,例如C++ 强调了其他方面,因为 C++ 不一定是面向对象的,而且更侧重于低级方面。因此,对象、POD 和内置原语之间的区别有时是有意义的(但有时又不是)。

【讨论】:

你是这么说的吗,我坚持将原始类型作为“非对象”是我 C++ 时代的一种保留,并且 int 是对象,即使在幕后它们的行为与“完全不同”类的实例”? 是的,这就是它的要点。 “对象”是一个概念,不一定与一个固定的实现相关联,通常它是“实例”的同义词(这可能不会让事情变得更好)。 嗯,这对我来说更好,我已经“看到了光明”,并将相应地更新我的答案。谢谢队友:) 根据这个定义,并非所有事物都是对象。例如,方法或运算符不是一等公民,因此不是对象。 @Konrad:我更喜欢使用与相关语言相关的术语。 C# 规范非常清楚地区分对象(类的实例)和值类型值。【参考方案4】:

您将对象与值或引用混淆了。基本上,一切都是对象。 Int 是一个对象,但它也是一个值类型。类实例是一个对象,但它也是一个引用类型。

方法不是对象,属性也不是。只是对对象进行操作。是的,几乎所有东西都继承自对象类。

【讨论】:

C# 规范区分对象(类的实例)和值类型值。 所有值类型都隐式派生自 Object 类:msdn.microsoft.com/en-us/library/s1ax56ch(VS.71).aspx【参考方案5】:

在 C#(以及一般的 OOP 中)中,我们有类型(类 - 引用、结构 - 值等)。这些是定义。而“对象”是给定类型的具体实例。

所以,如果我们从字面上理解这个问题,是的,实例化时一切都是对象。

混乱最有可能始于对所有事物的基类名称的错误选择。在.NET 中,这是 Object 类。

【讨论】:

“事物”这个词的定义不是很好 哦,它是:) - thing == System.Object :)【参考方案6】:

它们都被视为对象,但它们并不都是对象。混淆来自自动装箱。

更多信息请参见:http://en.wikipedia.org/wiki/Object_type

这种抽象显然使人们感到困惑。

【讨论】:

【参考方案7】:

我认为值类型不是对象。它们通过 CLR 以不同方式存储在内存中 - 值类型存储在堆栈中,对象存储在堆中。您可以将值类型强制转换为引用类型以使它们像对象一样工作,但是 CLR 从堆栈中取出值,将其包装在对象中,然后将其存储在堆中。这就是当你“装箱”一个变量时发生的事情。

【讨论】:

是的,这就是 Java 处理事情的方式,我的印象是 .net 是一样的。 澄清:值类型仅在不属于引用类型时才存储在堆栈中。作为引用类型一部分的值类型与实例的其余部分一起存储在堆中。【参考方案8】:

发件人:Value Types (C# Reference) - MSDN 3.5

所有值类型都是隐式派生的 来自 System.ValueType。

发件人:Value Type Class - MSDN 3.5

ValueType 覆盖虚拟 来自 Object 的更多方法 价值的适当实现 类型。

发件人:Enum Class - MSDN 3.5

这个类继承自 ValueType

继承层次如下:

System.Object System.ValueType System.Enum

结论:一切都是对象

【讨论】:

(假设:一切都是值类型。)【参考方案9】:

根据我读过的所有书籍,C# 中的一切都是对象。

有些是引用,有些是值类型。值类型对象继承自类ValueType。它们有不同的行为,但本质上是……对象。

这就是为什么您可以将 Int32 存储在对象变量中以及您可以在 .NET 中创建的所有内容中的原因。

更多详情...查看以下内容:http://msdn.microsoft.com/en-us/library/s1ax56ch(VS.71).aspx

所有值类型都是隐式派生的 来自 Object 类。

【讨论】:

C# 中的一切都不是对象。【参考方案10】:

虽然每个人似乎都在关注值类型与引用类型的争论,但我们忘记了 C# 中的一种类型,它既不是引用也不是值,它不是从对象派生的,也不能强制转换为对象: 指针。

与值和引用类型不同,指针不能转换为对象。

根据MSDN documentation on C# pointer types,

指针类型不继承自 对象并且不存在转换 指针类型和对象之间。 此外,装箱和拆箱不 支持指针。但是,您可以 在不同的指针之间转换 类型和指针类型之间和 整数类型。

【讨论】:

【参考方案11】:

简短回答:

答案取决于“对象”的定义。不同的语言对“对象”的定义不同,但是C#的权威定义是官方的C# Language Specification:

C# 语言的类型主要分为两大类:引用类型值类型。 (...) 值类型与引用类型的不同之处在于变量 的值类型直接包含它们的数据,而 引用类型存储对其数据的引用,后者是 称为对象

所以根据 C#,对象是引用类型的实例。因此,值类型值不是对象。所以在 C# 中一切都是对象是不正确的。

但是:

C#的类型 系统是统一的,因此任何类型的值都可以被视为 目的。 (...) 值类型的值被视为对象 执行装箱和拆箱操作(第 9.3.12 节)。

因此,值类型可以通过装箱来处理作为对象(有效地变成引用类型)。但是未装箱的值类型本身并不是一个对象。

CLR Specification [PDF] 使用与 C# 非常相似的定义:

object:引用类型的实例。一个物体比它有更多的东西 一个值。一个对象是自键入的;它的类型显式存储在 它的代表。它有一个与众不同的身份 其他对象,并且它具有存储其他实体的插槽(可以 是对象或值)。虽然其插槽的内容可以 改变了,对象的身份永远不会改变。

所以在 CLR 术语中,值类型值也不是对象。

【讨论】:

Github 上的规范现已上线,因此可以在Types 上直接添加相关文章的链接【参考方案12】:

解决语义,当我们已经有一个非常好的、明确的术语时,为什么要重载“对象”这个词,以便它意味着“引用类型” -> “引用类型”,以及何时,通过重载这个词“ Object”以这种方式创建了这个线程演示的混乱......即,所有类型(包括值类型)继承“System.Object”类型中定义的实现这一事实之间的不匹配。显然,这充其量是不必要的,最坏的情况是极其混乱。即使 MS 文档有时在这个问题上令人困惑这一事实也不能成为传播混乱的借口。

更简单、更清晰的是,只定义和使用术语“对象”来表示任何类型、值或引用的实例,并使用短语“引用类型”来描述使用指针变量并具有它们的类型的类型状态存储在堆上...

【讨论】:

【参考方案13】:

数字 2 不是对象。

【讨论】:

但它存储为 Int32,它是一个对象。 但直到需要时它才会被装箱到对象中。因此,它并不总是一个对象。 您的意思是在程序运行之前它不会被装箱到对象中?还是正在解释代码? 我认为他的意思是它被存储为原始类型,然后在使用时自动装箱。在我看来,这使它与对象不同。 如果您有代码int x = 2 + 3;x 和 2 和 3 都不是对象。但是,将 Object.equals( 2, 3 ) 框 2 和 3 调用为两个对象。【参考方案14】:

这是对两个世界的讨论:语言和记忆。

对我来说,语言就像一个抽象层,而术语对象属于这个抽象层。我认为在内存组织方面谈论对象没有意义,如果在谈论内存时确实使用“对象”术语,那么实际上是从不同的抽象层借用了这个术语。因此,您不应该忘记它的来源。

如果我们谈论的是 C#,我不明白为什么有人会使用内存组织作为论据。当然,如果我要向某人回答这个问题,我会说“是的,在 C# 中,一切都是对象,但您也应该知道,在引擎盖下,它可能会根据...而不同地工作。”

这可能会引发一个有趣的争论,但也可能会引起一些人的注意: 在类似的讨论中,人们可以说实际上没有面向对象的编程,只有过程编程。你的 CPU 理解对象吗? 更好的是,实际上没有软件,只有不同的硬件状态:)

我的观点是,某些术语不会转化为其他抽象层,您应该将讨论集中在它所属的位置(在这种情况下是一种语言,而不是记忆)。

即使是这个问题的作者也表示: “请注意,这个问题涉及 C# 不一定是 .NET 以及它如何处理后台数据(显然都是 1 和 0。)”

【讨论】:

【参考方案15】:

值类型不是对象,它们遵循不同的复制语义、不同的传递语义,并且必须包装在一个类(对象)中才能被这样对待。

编辑:我认为这个论点有些模糊,因为您必须限定“对象”的含义。对象只是继承自 Object 的东西,还是遵循 Object 的使用语义的东西?还是我们在谈论对象的最一般定义,它是指可以包含数据和对该数据进行操作的任何东西?

【讨论】:

你不是说,值类型不是引用类型吗?它们当然是对象。【参考方案16】:

考虑到问题是指 OOP 意义上的 Object,答案是:

技术的角度来看,答案是:

教条的角度来看,答案是:是的

解释:

从技术上讲,值类型(基元或结构)不是对象,除非是“装箱”形式,而是因为 .Net 通过装箱/拆箱(创建一个包含值,并且是从 Object 派生的),这意味着值类型可以被视为对象和简单值。

所以值类型本质上是双重的,它们表现为对象。 .Net 中的值在需要时是对象,在其他情况下它们不是对象。

考虑到技术方面的正确答案是“.Net 中的一切都就像对象一样”。

教条式的答案是“一切都是对象”。

【讨论】:

【参考方案17】:

有这么多不同答案的原因之一是这个问题非常不精确。 “一切”是什么意思?它真的意味着每个 C# 语言元素吗?那么答案显然是“否”:运算符不是对象,“using”关键字不是对象,cmets 不是对象,等等。

但如果这不是本意,那又是什么意思呢?也许“除了那些显然不是类的东西之外的所有东西”?这显然没有帮助,因为不同的人对什么是“显而易见的”有不同的看法。尽管如此,大多数答案似乎都遵循这种固执己见的解释。

另一个混淆来源是“对象”一词。什么是对象?这个术语没有唯一的通用定义,不同的人似乎以不同的方式使用它。 C# 语言中唯一的正式定义是 System.Object 类型的定义,以及哪些其他类型派生自它,哪些不是。该文档很容易获得,并且不能说更多。

【讨论】:

感谢您为 *** 提供答案。但是,这个答案过度概括超出了解决问题所需的范围。从现有答案中可以看出,C# 中的“对象”有 两种 可能的含义。 一个含义ValueTypeObject成员 可以在“值类型”上调用这一事实有关。根据这个定义,“值类型”“对象”。 第二个含义来自 C# 规范,它明确地将“引用类型的实例”称为“对象”,不包括值类型。两者都是有效的观点。

以上是关于.NET 中的一切都是对象吗?的主要内容,如果未能解决你的问题,请参考以下文章

面向对象

一切都是对象

可以在 C# 中的委托上传递 System.Net.Mail.MailMessage 对象吗?

javascript中的Function和Object

第二章 - 一切都是对象

python里的类与一切事物都是对象