Я попробовал решение от @Aidan Ryan с небольшой настройкой, чтобы избежать другой проблемы, связанной с пользовательским интерфейсом. Я перемещаю «SetWindowPos» в OnNCPaint () и вызываю функцию только один раз.
И 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
// 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 #. Я рекомендую использовать псевдонимы, если нет другой причины.
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 # достаточно хороша, чтобы избежать боксов в большинстве случаев.)
В C # лучший способ разделить объекты - это «Типы значений», которые похожи на примитивы - int
s, bool
s и т. Д. и «Типы ссылок» - классы и т. д.