【问题标题】:.NET Primitive types implementation.NET 基本类型实现
【发布时间】:2012-07-29 23:00:49
【问题描述】:

我对 .NET 和 Java 中的面向对象编程有很好的理解。我了解 Java 和 .NET 中的所有内容都源自一个对象。

我的问题是关于原始类型的。我知道原始类型不是从 Object 派生的,我也知道它们有多宽,例如一个字节是 8 位,一个整数是 32 位。我也明白拳击是对象的等价物。

我的问题是:如果原始类型不是对象,它们是如何实际实现的?我对编译器理论有一点了解,但可能需要深入研究。

在我的 SCJP 书中它说:“如果排除原始类型,那么 Java 中的一切都是对象”。在我的 .NET 书中也有同样的说法。他们没有深入细节。

我在这个问题中谈到了 Java,因为我来自 Java 背景并且 Java 是面向对象的(如 .net)。但是,我正在寻找特定于 .NET 的答案。

【问题讨论】:

标签: .net vb.net


【解决方案1】:

在 Java 中,原始类型不是从类型 java.lang.Object 派生的。因此,有额外的类(例如 java.lang.Integer 用于 int 原始类型)包装原始类型,因此它们可以存储在引用变量中。

CLR 也知道原始类型,但它们被定义为结构。所有结构内部都派生自System.ValueType,而后者又继承自System.Object。 C# 中的名称intdouble 等只是.NET 框架中System.Int32System.Double 等结构类型的别名。 CLR 中对这些类型进行了特殊处理,但与 Java 不同的是,它们是统一类型层次结构的一部分。

Java VM 和 CLR 在这方面有很大不同。在 Java 中,intjava.lang.Integer 类型是完全独立的,除了编译器知道如何在它们之间进行转换。在 CLR 中,只有一种类型,System.Int32,可以以装箱和未装箱的形式出现。

【讨论】:

  • 谢谢。 +1 用于引用 System.ValueType 并确认这些是结构。
  • 由于引用了 Java 和 .NET,我已将此标记为答案。
  • 顺便说一句:如果您真的想了解 CLR 的内部结构,请尝试“CLR via C#”一书。当涉及到示例时,它可能专注于 C#,但 CLR 的行为方式始终相同,无论您使用 C#、VB.NET 还是任何其他编译为 CIL 字节码的语言。
【解决方案2】:

这取决于您戴的眼镜。如果你有“实现”眼镜,那么你会看到任何引用类型值都有一个对象头。这使得它们继承 System.Object。对象头有两个字段,同步块存储各种信息,如哈希码和调用 Monitor.Enter() 的线程 ID。重要的字段是第二个字段,方法表指针。标识对象类型。方法表包含该类方法的地址,它总是以 Object 实现的方法 Equals、GetHashCode 和 ToString 开头。

戴着同样的眼镜,一个值类型的值没有有这个对象头。它只占用存储值所需的空间量。一个字节用于 bool,两个字节用于 Char,四个字节用于 int,等等。这使得值类型非常高效。

您可以戴的第二副眼镜是“类型系统”眼镜。值类型值始终可以转换为对象。然后回来。这种转换称为装箱转换。返回称为拆箱。快点戴上实现眼镜,你会发现你确实得到了一个带有这两个字段的对象,方法表指针标识了值类型。方法表有额外的方法指针。与 IConvertible 类似,这是一个由值类型实现的接口。除了这两个字段之外,对象的其余部分由值类型值位占用。在装箱转换之前它仍然是一个简单值时具有的相同位。装箱的对象和所有引用类型的对象一样,都存在于垃圾收集堆上。

在绝大多数情况下,C# 或 VB.NET 编译器会完全自动应用装箱转换。您不必自己在代码中编写演员表。例如,您可以调用 ToString 或 IConvertible 方法之一,您将免费获得装箱转换。

这产生了值类型继承自 System.Object 的错觉。这是一个相当不错的错觉,任何戴“类型系统”眼镜的人都会坚持认为值类型绝对继承自 Object。如果您戴“实施”眼镜,那么您往往会担心拳击。它经过高度优化,但肯定不是免费的。进行转换会花费 cpu 周期,并且装箱的值类型值会占用更多空间并产生垃圾。泛型集合类型已完全取代旧的 System.Collection 类的原因之一。

【讨论】:

  • 这个答案非常有帮助+1。很遗憾我已经将问题标记为已回答。
  • 也许您可以在这里查看我的其他 .NET 问题:stackoverflow.com/questions/11689959/…。也许我可以对此表示赞赏。
【解决方案3】:

这是完全错误的。在 .NET 中,“原始”类型(= 值类型)确实实际上派生自 System.Object。但是,它们仅在涉及类型系统时才这样做。它们的处理方式仍然不同于引用类型。

这只是通过编译器和运行时中的特殊处理来处理。最直接的实现是引用类型是通过指向堆分配存储的指针实现的,而值类型根本不是。但是,请注意,这严格来说是 Microsoft 的 CLR (.NET) 实现的一个实现细节。其他实现可能会以不同方式处理此问题。

【讨论】:

  • 谢谢。请在 Oded 的回答下查看我的评论。
  • @w0051977 奥德说了什么。 Java是不同的。 .NET 确实没有“原始类型”之类的东西,它只有引用类型和值类型,但都派生自 Object
  • 感谢康拉德鲁道夫。 +1 用于确认“没有这样的事情”和“Java 是不同的”。
【解决方案4】:

简答:原始类型派生自 System.Object,它的实现方式与 .net 中的任何其他类型一样。

长答案: 但是,基本类型在 .NET 中被称为是因为支持它们的类被编译器识别为内置类型。此外,原始类型不是引用类型,它们是值类型(在 .net 中也称为轻量级类型),因此它们不是分配在托管堆上而是分配在线程堆栈上。

例如:

var a = new Int32(16);
var b = 16;

两个声明都会生成相同的 IL 指令。编译器会将 b = 16 解释为 b = new Int32(16)。 编译器将对涉及内置轻量类型/基元的代码进行一些优化,例如:

int a = 1 + 2;

将编译为:

int a = 3;

当然还有其他不那么明显的优化,但我欠你一个很好的解释。

重点是类型和轻量级类型(原语所属)之间的区别。这是一篇讨论这两种类型之间差异的文章,是一个很好的起点: http://msdn.microsoft.com/en-us/magazine/cc301569.aspx

【讨论】:

  • 谢谢,为文章+1,并在答案中引用堆和堆栈。
【解决方案5】:

我知道原始类型不是从 Object 派生的

它们确实继承自对象。例如,请参阅 System.Int32(这是 VB.NET 将 Integer 翻译成的内容)。

就像 Java 一样,在 .NET 中所有类型都继承自 Object,包括原始类型。

您所看到的语言原始类型在编译期间被转换为从 Object 继承的相应 CLR 类型。

【讨论】:

  • 谢谢。这是我为 Java 阅读的书:google.co.uk/…。第 542 页说:“如果排除原始类型,那么 Java 中的一切都是对象”。这在 .NET 中有何不同?
  • @w0051977 - 是的。 .NET 中的每种类型都是一个对象。
  • 你能确认 .NET 在这个意义上与 Java 不同吗?
  • @CodesInChaos 你提出了一个观点,虽然...... “指针是特殊类型的变量”。 CLR 仅提供指针的加法和减法运算。但是指针是一个特例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-07-21
  • 2022-07-11
  • 1970-01-01
  • 2012-12-12
  • 1970-01-01
  • 1970-01-01
  • 2020-03-13
相关资源
最近更新 更多