Отличаются ли примитивные типы в Java и C#?
Я вручную конвертирую код из Java в C# и борюсь с (что я называю) примитивными типами (см., Например, автобокс и распаковка ведут себя по-разному в Java и C#). Из ответов я понимаю, что double
(C#) и Double
(C#) эквивалентны и double
(C#) также может использоваться в контейнерах, например, как ключ в словаре. Тем не мение, double
(Java) нельзя использовать в контейнерах, таких как HashMap, поэтому он автоматически упакован в Double
(Джава).
- Является
double
(C#) примитив или объект? - Если это примитив, что заставляет его вести себя иначе, чем
double
(Джава)?
double
(C#) не может быть установлен в ноль, если это не сделано nullable
,
- Является
double?
(C#) эквивалентноDouble
(Джава)? Они оба упоминаются как объекты?
(Полезно ли использовать термин "первоклассный объект" в этом обсуждении?)
3 ответа
И 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# обрабатывает "типы значений, допускающие обнуляемость", Nullable<T>
тип обертки. Кроме того, C#, как и Java, имеет неявные преобразования из типа значения T
в Nullable<T>
с ограничением на то, что 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
// 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#: Значение против Типов ссылок. Обратите внимание, что в приведенном выше примере "подъема" переменная имеет тип object
: object
является "корневым типом" в C# (родительский тип ссылочных типов и типов значений), а не специализированным типом значений.
1 Язык C# поддерживает фиксированный набор псевдонимов для примитивных / общих типов, которые разрешают доступ к "дружественным строчным" именам типов. Например, double
это псевдоним для System.Double
а также int
это псевдоним для System.Int32
, Если не другой Double
тип импортируется в область, double
а также Double
будет ссылаться на тот же тип в C#. Я рекомендую использовать псевдонимы, если нет причин поступать иначе.
Nullable<double>
(ака double?
) в C# не совпадает с 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
) и он будет упакован в случае необходимости, или просто используется напрямую. (Реализация Generics в C# достаточно хороша, чтобы в большинстве случаев избежать бокса.)
В C# лучший способ отделить объекты - это "Типы значений", которые похожи на примитивы - int
s, bool
s и т. д. и "Типы ссылок" - классы и т. д.