Are primitive types different in Java and C#?

c#, first-class, java, primitive

Solution

Both C# and Java have primitive (or "value") types: int, double, float, etc...

However, after this C# and Java tend to divide.

Java has wrapper Class types for all of the primitive types (which is a small finite set in Java) which allows them to be treated as Object. `double/Double`, `int/Integer`, `bool/Boolean`, etc. These wrapper types are reference-types (read: Classes) and, as such, `null` is a valid value to assign to such typed expressions/variables. Recent versions of Java (1.5/5+) add in implicit coercions from primitives to their corresponding wrapper.

// 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# doesn't provide a such a direct wrapping1 - in part, because C# supports an infinite set of value types via structures; rather, C# handles "nullable value types" by introduction of a `Nullable<T>` wrapper type. In addition C#, like Java, has implicit conversions from the value type `T` to `Nullable<T>`, with the restriction that T is "not a nullable type" itself.

// 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

Note that `Nullable<T>` is also a value type and thus follows the standard structure rules for when/if a value is "on the stack" or not.

In response to the comment:

Absolutely correct, Nullable being a value-type does allow it to have a more compact memory footprint in certain cases as it can avoid the memory overhead of a reference type: What is the memory footprint of a Nullable<T>. However it still requires more memory than the non-Nullable type because it has to remember if the value is, well, null, or not. Depending upon alignment issues and VM implementation, this may or may not be significantly less than a "full" object. Also, since values in C#/CLR are reified, consider any lifting operations that must be performed:

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

The article Java Tip 130: Do you know your data size? talks about reference type memory consumption (in Java). One thing to note is that the JVM has specialized versions of Arrays internally, one for each primitive type and for Objects (however, please note that this article contains some misleading statements). Note how the Objects (vs. primitives) incur extra memory overhead and the byte alignment issues. C# however, can extend the optimized-array case for `Nullable<T>` types vs. the the limited special-cases the JVM has because `Nullable<T>` is itself just a structure type (or "primitive").

However, an Object, only requires a small fixed size to maintain a "reference" to it in a variable slot. A variable slot of type `Nullable<LargeStruct>` on the other hand, must have space for `LargeStruct+Nullable` (the slot itself may be on the heap). See C# Concepts: Value vs Reference Types. Note how in the "lifting" example above the variable is of type `object`: `object` is the "root type" in C# (parent of both reference types and value types) and not a specialized value type.

1 The C# language supports a fixed set of aliases for primitive/common types that allow access to "friendly lowercase" type names. For instance, `double` is an alias for `System.Double` and `int` is an alias for `System.Int32`. Unless a different `Double` type is imported in scope, `double` and `Double` will refer to the same type in C#. I recommend using the aliases unless there is a reason to do otherwise.

Problem

I am manually converting code from Java to C# and struggling with (what I call) primitive types (see, e.g. Do autoboxing and unboxing behave differently in Java and C#). From the answers I understand that `double` (C#) and `Double` (C#) are equivalent and `double` (C#) can also be used in containers, e.g. as a key in a Dictionary. However, `double` (Java) cannot be used in containers like HashMap which is why it is auto-boxed to `Double` (Java). - Is `double` (C#) a primitive or an object? - If it's a primitive what makes it behave differently from `double` (Java)? `double` (C#) cannot be set to null unless it is made `nullable`. - Is `double?` (C#) equivalent to `Double` (Java)? Are they both referred to as objects? (Is the use of the term "first-class object" useful in this discussion?)

Original source

Related problems