14

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

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

double(C#) 不能设置为 null,除非它是 made nullable

  1. (C#)是否double?等同于Double(Java)?它们都被称为对象吗?

(在这个讨论中使用术语“第一类对象”有用吗?)

4

3 回答 3

17

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

然而,此后C#和Java趋于分道扬镳。

Java 为所有原始类型(在 Java 中是一个小的有限集)提供了包装类类型,这允许它们被视为对象。 double/Double, int/Integer,bool/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# 通过引入包装类型来处理“可空值类型” 。此外,与 Java 一样,C# 具有从值类型到的隐式转换,其限制是 T 本身是“不可为空的类型”。Nullable<T>TNullable<T>

// C#
Nullable<bool> 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<T>也是一个值类型,因此遵循标准结构规则,何时/如果一个值“在堆栈上”或不“在堆栈上”。

回应评论:

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

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

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

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


1 C# 语言支持原始/通用类型的一组固定别名,允许访问“友好的小写”类型名称。例如,double是 的别名System.Doubleint是 的别名System.Int32。除非Double在作用域中导入了不同的类型,double并且Double在 C# 中将引用相同的类型。我建议使用别名,除非有理由不这样做。

于 2009-10-21T01:58:04.550 回答
10

Nullable<double>double?C# 中的 (aka )JavaDouble中的 a 不同。

在 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),您可以在其中使用任何对象引用(例如,作为 a 中的键Dictionary),如果需要,它将被装箱,或者直接使用。(C# 的泛型实现足以在大多数情况下避免装箱。)

于 2009-10-21T00:07:45.537 回答
1

在 C# 中,分离对象的最佳方法是通过“值类型”,它有点像原语 - ints、bools 等和“引用类型” - 类等。

于 2009-10-21T00:09:51.010 回答