Отличаются ли примитивные типы в Java и C #?

Я попробовал решение от @Aidan Ryan с небольшой настройкой, чтобы избежать другой проблемы, связанной с пользовательским интерфейсом. Я перемещаю «SetWindowPos» в OnNCPaint () и вызываю функцию только один раз.

14
задан Community 23 May 2017 в 12:25
поделиться

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 # обрабатывает «типы значений, допускающие значение NULL» путем введения типа оболочки Nullable . Вдобавок C #, как и Java, имеет неявные преобразования из типа значения T в Nullable с ограничением, что T сам «не допускает значения NULL».

// 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 также является типом значения и, таким образом, следует стандартным структурным правилам для того, когда / если значение находится «в стеке» или нет.

В ответ на комментарий :

Абсолютно правильно, Nullable, являясь типом значения, позволяет ему иметь более компактный объем памяти в некоторых случаях , поскольку он может избежать накладных расходов на память ссылочного типа: Что такое объем памяти Nullable . Однако ему по-прежнему требуется больше памяти, чем типу, отличному от Nullable, потому что он должен помнить, является ли значение нулем или нет. В зависимости от проблем выравнивания и реализации виртуальной машины это может быть или не быть значительно меньше, чем «полный» объект. Кроме того, поскольку значения в C # / CLR реифицируются, рассмотрите любые операции подъема, которые необходимо выполнить:

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

В статье Java Tip 130: Знаете ли вы свой размер данных? говорится о потреблении памяти ссылочного типа (в Java). Следует отметить, что внутри JVM есть специализированные версии массивов, по одной для каждого примитивного типа и для объектов (однако, обратите внимание, что эта статья содержит некоторые вводящие в заблуждение утверждения ). Обратите внимание, как объекты (по сравнению с примитивами) несут дополнительные накладные расходы на память и проблемы с выравниванием байтов. Однако C # может расширить случай оптимизированного массива для типов Nullable по сравнению с ограниченными частными случаями, которые есть в JVM, потому что Nullable сам по себе является просто типом структуры (или «примитив»).

Однако объекту требуется только небольшой фиксированный размер, чтобы поддерживать «ссылку» на него в переменном слоте. Слот переменной типа Nullable < LargeStruct> , с другой стороны, должен иметь место для LargeStruct + Nullable (сам слот может находиться в куче). См. Концепции C #: значение и ссылочные типы . Обратите внимание, что в приведенном выше примере «подъема» переменная имеет тип объект : объект является «корневым типом» в C # (родительский для ссылочных типов и типов значений) и , а не специализированный тип значения.


1 Язык C # поддерживает фиксированный набор псевдонимов для примитивных / общих типов, которые позволяют получить доступ к именам типов в "понятном нижнем регистре". Например, double - это псевдоним для System.Double , а int - это псевдоним для System.Int32 . Если в область не импортирован другой тип Double , double и Double будут ссылаться на один и тот же тип в C #. Я рекомендую использовать псевдонимы, если нет другой причины.

17
ответ дан 1 December 2019 в 09:32
поделиться

Nullable (также известный как double? ) в C # - это не то же самое, что Double в Java.

До того, как в 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 # есть автоматическая упаковка / распаковка для всех типов значений, а также все типы значений являются производными от объекта .

Таким образом, вы можете использовать тип значения (например, double ), где вы можете использовать любую ссылку на объект (например, как ключ в Словаре ), и при необходимости он будет помещен в рамку, или просто использовать напрямую. (Реализация Generics в C # достаточно хороша, чтобы избежать боксов в большинстве случаев.)

10
ответ дан 1 December 2019 в 09:32
поделиться

В C # лучший способ разделить объекты - это «Типы значений», которые похожи на примитивы - int s, bool s и т. Д. и «Типы ссылок» - классы и т. д.

1
ответ дан 1 December 2019 в 09:32
поделиться