Подтвердить что ты не робот

Являются ли примитивные типы разными в Java и С#?

Я вручную конвертирую код из Java в С# и борюсь с примитивными типами (то, что я называю) (см., например, Выполняют ли autoboxing и unboxing по-разному в Java и С#). Из ответов я понимаю, что double (С#) и double (С#) эквивалентны, а double (С#) также может использоваться в контейнерах, например. как ключ в словаре. Тем не менее, double (Java) не может использоваться в контейнерах, таких как HashMap, поэтому он автоматически помещается в поле double (Java).

  • Является ли double (С#) примитивным или объектом?
  • Если это примитив, что заставляет его вести себя иначе, чем double (Java)?

double (С#) не может быть установлено в значение null, если только оно не сделано nullable.

  1. Является ли double? (С#) эквивалентным double (Java)? Они оба называются объектами?

(Используется ли термин "первоклассный объект" в этом обсуждении?)

4b9b3361

Ответ 1

Оба С# и Java имеют примитивные (или "значения" ) типы: int, double, float и т.д.

Однако после этого С# и Java имеют тенденцию к делению.

Java имеет типы классов-оболочек для всех примитивных типов (который представляет собой небольшой конечный набор в Java), который позволяет им обрабатываться как Object. double/Double, int/Integer, bool/Boolean и т.д. Эти типы-оболочки являются ссылочными типами (read: Classes) и, как таковые, 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!

С# не обеспечивает такую ​​прямую упаковку 1 - частично, потому что С# поддерживает бесконечный набор типов значений через структуры; скорее, С# обрабатывает "типы допустимых значений" путем введения типа оболочки Nullable<T>. Кроме того, С#, как и 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 <T> . Однако для него все еще требуется больше памяти, чем тип, отличный от Nullable, потому что он должен помнить, имеет ли значение значение нуль или нет. В зависимости от проблем с выравниванием и реализации виртуальной машины это может быть или не быть значительно меньше, чем "полный" объект. Кроме того, поскольку значения в С#/CLR подтверждены, рассмотрите любые операции по подъему, которые должны выполняться:

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

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

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


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

Ответ 2

Nullable<double> (aka double?) в С# не совпадает с Double в Java.

Прежде чем Java имел автобоксинг/распаковку, вам пришлось вручную конвертировать между примитивами и первоклассными объектами:

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

В Java 1.5, который изменился, вы можете просто сделать:

Double dblObj = 2.0;
double dblPrim = dblObj;

И Java вставляет код, чтобы автоматически отразить приведенный выше пример.

С# отличается тем, что существует неограниченное количество "примитивных" типов (что CLR вызывает типы значений). Они ведут себя в основном как Java-примитивы, используя семантику значений. Вы можете создавать новые типы значений с помощью ключевого слова struct. С# имеет autoboxing/unboxing для всех типов значений, а также делает все типы значений из Object.

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

Ответ 3

В С# наилучшим способом разделения объектов являются "Типы значений", которые являются похожими на примитивы - int s, bool s и т.д. и "Типы ссылок" - классы и т.д.