0

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

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

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

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

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

4

5 回答 5

3

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

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

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

于 2012-07-29T10:58:09.010 回答
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 类的原因之一。

于 2012-07-29T14:46:10.173 回答
1

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

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

于 2012-07-29T10:06:12.587 回答
1

简短回答:原始类型派生自 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

于 2012-07-29T10:59:22.223 回答
0

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

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

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

您所看到的语言原始类型在编译期间被转换为继承自Object.

于 2012-07-29T10:05:25.940 回答