Java和C#中的原始类型是否不同?

我手动将代码从Java转换为C#并与(我称之为)原始类型进行斗争(例如,参见自动装箱和拆箱在Java和C#中表现不同 )。 从答案中我理解double (C#)和Double (C#)是等价的, double (C#)也可以用在容器中,例如作为Dictionary中的键。 但是, double (Java)不能在像HashMap这样的容器中使用,这就是它被自动装箱到Double (Java)的原因。

  1. double (C#)是一个原语还是一个对象?
  2. 如果它是一个原始的,它使它的行为与double (Java)不同?

double (C#)除非可以为空,否则不能设置为null。

  1. double? (C#)相当于Double (Java)? 它们都被称为物体吗?

(在这次讨论中,“第一类对象”一词的用法是否有用?)

C#和Java都有原始(或“值”)类型:int,double,float等…

但是,在此之后,C#和Java倾向于分裂。

Java为所有基本类型 (Java中的一个小型有限集)提供了包装类类型,允许将它们视为对象。 double/Doubleint/Integerbool/Boolean等。这些包装类型是引用类型(读取:类),因此, null是分配给此类型表达式/变量的有效值。 Java(1.5 / 5 +)的最新版本添加了从原语到其相应包装器的隐式强制。

 // Java Boolean b = true; // implicit conversion boolean -> Boolean (Java 5+) Boolean b = null; // okay, can assign null to a reference type boolean n = null; // WRONG - null is not a boolean! 

C#没有提供这样的直接包装1 – 部分原因,因为C#通过结构支持一组无限的值类型 ; 相反,C#通过引入Nullable包装器类型来处理“可空值类型”。 此外,与Java一样,C#具有从值类型TNullable隐式转换,其限制是T本身“不是可空类型”。

 // C# Nullable b = true; // implicit conversion bool -> bool? bool? b = true; // short type syntax, implicit conversion bool? b = null; // okay, can assign null as a Nullable-type bool b = null; // WRONG - null is not a bool 

注意, Nullable也是一个值类型,因此遵循标准结构规则,用​​于何时/如果值是“在堆栈上”或不是。

回应评论:

绝对正确,Nullable是一个值类型确实允许它在某些情况下具有更紧凑的内存占用因为它可以避免引用类型的内存开销: Nullable 的内存占用量是多少 。 但是它仍然需要比非Nullable类型更多的内存,因为它必须记住值是否为null。 根据对齐问题和VM实现,这可能会或可能不会明显小于“完整”对象。 此外,由于C#/ CLR中的值已确定,请考虑必须执行的任何提升操作:

 // C# object x = null; x = (bool?)true; (x as bool?).Value // true 

文章Java技巧130:你知道你的数据大小吗? 讨论引用类型内存消耗(在Java中)。 需要注意的一点是,JVM内部有专门的Arrays版本,每个基本类型和对象一个(但请注意,本文包含一些误导性的陈述 )。 注意对象(与原语相比)如何产生额外的内存开销和字节对齐问题。 但是,C#可以扩展Nullable类型的优化数组的情况,而不是JVM所具有的特殊情况,因为Nullable本身只是一种结构类型(或“原始”)。

但是,一个Object只需要一个小的固定大小来在变量槽中维护它的“引用”。 另一方面, Nullable类型的变量槽必须具有LargeStruct+Nullable空间(槽本身可能在堆上)。 请参阅C#概念:值与参考类型 。 请注意,在上面的“提升”示例中,变量的类型是objectobject是C#中的“根类型”(两种引用类型和值类型的父类),而不是特殊的值类型。


1 C#语言支持原始/公共类型的一组固定别名 ,允许访问“友好的小写”类型名称。 例如, doubleSystem.Double的别名, intSystem.Int32的别名。 除非在范围内导入不同的Double类型,否则doubleDouble将引用C#中的相同类型。 除非有理由不这样做,否则我建议使用别名。

C#中的Nullable (又名double? )与Java中的Double

在Java进行自动装箱/拆箱之前,您必须手动转换基元和第一类对象:

 Double dblObj = new Double(2.0); double dblPrim = dblObj.doubleValue(); 

在Java 1.5中发生了变化,所以你可以这样做:

 Double dblObj = 2.0; double dblPrim = dblObj; 

Java会自动插入代码来镜像上面的例子。

C#是不同的,因为有无限数量的“原始”类型(CLR称之为值类型 )。 这些行为大多类似于Java的原语,使用值语义 。 您可以使用struct关键字创建新的值类型。 C#对所有值类型都有自动装箱/取消装箱,并且还使所有值类型都来自Object

因此,您可以使用值类型(如double ),您可以使用任何对象引用(例如,作为Dictionary的键),如果需要,它将被装箱,或者直接使用。 (在大多数情况下,C#的Generics实现足以避免装箱。)

在C#中,分离对象的最佳方法是“值类型”,它有点像基元 – int s, bool s等,“引用类型” – 类等。