Можно согласиться с тем, что хотя концепция достаточно простая, обход включает развитую технику и она может не сразу стать понятной новичку, в конце концов нам требуется только список констант; C#, в противоположность, предоставляет встроенную поддержку перечислении, которая обеспечивает также безопасность типов. Чтобы объявить в C# перечисление, используется ключевое слово
enum
. В своей простейшей форме
enum
может выглядеть как следующий код:
public enum Status {
Working,
Complete,
BeforeBegin
}
В приведенном выше случае первое значение равно 0 и
enum
увеличивает
значения.
Complete
будет 1 и т.д. Если по какой-то причине требуется, чтобы
enum
представлял другие значения, можно сделать это, присваивая такие значения следующим образом:
public enum Status {
Working = 131,
Complete = 129,
BeforeBegin = 132
}
Имеется также возможность использовать другие числовые целые типы 'наследуя' от
long
,
short
или
byte
.
int
всегда является типом по умолчанию. Эта концепция проиллюстрирована ниже:
public enum Status : int {
Working,
Complete,
BeforeBegin
}
public enum SmallStatus : byte {
Working,
Complete,
BeforeBegin
}
public enum BigStatus : long {
Working,
Complete,
BeforeBegin
}
Обратите внимание, что существует большое различие между этими тремя перечислениями, связанное напрямую с размером типа данных, от которого они наследуют.
byte
в C#, например, может содержать 1 байт памяти. Это означает, что
SmallStatus
не может с одержать более 255 констант или задать значение любой своей константы больше 255. Следующий листинг показывает, как можно использовать оператор
sizeof
для идентификации различий между версиями
Status
:
int х = sizeof(Status);
int у = sizeof(SmallStatus);
int Z = sizeof(BigStatus);
Console.WriteLine("Regular size:\t{0}\nSmall size \t{1}\nLarge size:\t{2}", x, y, z);
После компиляции листинг создаст результаты, показанные ниже:
Regular size: 4
Small size: 1
Large size: 8
Структуры
Одним из основных различий между структурой C# (идентифицируемой ключевым словом
struct
) и классом является то, что то умолчанию
struct
передается посредством значения, в то время как объект передается по ссылке. Как хорошо известно, объекты создаются в куче, в то время как переменные, которые на них ссылаются, хранятся в стеке. Структуры, со своей стороны, создаются и хранятся в стеке. Их аналога в Java не существует. Структуры имеют конструкторы и методы, у них могут быть индексаторы, свойства, операторы и даже вложенные типы. С помощью
struсt
создаются типы данных, которые ведут себя таким же образом, как встроенные типы. Ниже приведен пример использования структур:
public struct WroxInt {
int internalVal;
private WroxInt(int x) {
internalVal = x;
}
public override string ToString {
return Int.ToString(internalVal);
}
public static impicit operator WroxInt(int x) {
return new WroxInt(x);
}
}
public static void UseWroxInt {
WroxInt wi = 90;
Console.WriteLine(wi);
}
Этот
пример показывает типы, которыми владеют мощные структуры.
WroxInt
используется почти так же, как и встроенный тип
int
. Как известно, не существует способа сделать что-нибудь подобное в Java. Ряд других достоинств и ограничений, связанных с использованием структур, представлен ниже:
□
struct
нельзя наследовать от другой
struct
или от класса.
□
struct
не является базой для класса
□ Хотя
struct
может oбъявлять конструкторы, эти конструкторы должны получать не меньше одного аргумента.
□ Члены
struct
не могут иметь инициализаторов.
□ Возможно создание экземпляра
struct
без использования ключевого слова
new
.
□
struct
может реализовывать интерфейсы.
Атрибуты используются со структурами чтобы добавить им дополнительную мощь и гибкость. Атрибут
StructLayout
в пространстве имен
System.Runtime.InteropServices
, например, применяется для определения компоновки полей в
struct
. Это свойство подходит и для создания структуры, аналогичной по функциональности
union
в С/C++,
union
является типом данных, члены которого находятся в одном блоке памяти. Он может использоваться для хранения значений различных типов в одном блоке памяти.
union
годится и в том случае, когда неизвестно, каким будет тип полученных значений. Конечно, никакого рeaльного преобразования не происходит, фактически не существует никакие базовых проверок допустимости данных. Один и тот же набор битов интерпретируется различным образом. Рассмотрим пример того, как
union
создается с помощью
struct
:
[StructLayout(LayoutKind.Explicit)]
public struct Variant {
[FieldOffset(0)] public int intVal;
[FieldOffset(0)] public string stringVal;
[FieldOffset(0)] public decimal decVal;
[FieldOffset(0)] public float floatVal;
[FieldOffset(0)] public char charVal;
}
Атрибут
FieldOffset
, применяемый к полям, используется для задания физического расположения указанного поля. Задание начальной точки каждого поля как 0 гарантирует, что любое сохранение данных в одном поле перезапишет практически любые данные, которые там находятся. Отсюда следует, что общий размер полей равен размеру наибольшего поля, в данном случае
decimal
.
Ссылочные типы
Ссылочные типы хранят ссылку на данные, которые существуют в куче. Только адреса памяти хранимых объектов сохраняются в стеке. Тип объекта, массивы, интерфейсы тип класса и делегаты являются ссылочными типами. Объекты, классы и отношения между ними не отличаются в Java и C#. Интерфейсы и их использование также похожи в обоих языках. Одно из основных различий, которое, вероятно, уже встречалось, состоит в том, что C# не имеет ключевых слов
extends
и
implements
. Оператор двоеточия (
:
) заменяет оба ключевых слова Java, и, как было показано ранее, директива
using
аналогична инструкции Java
import
. Строки тоже используются одинаково в C# и Java. C# вводит также новый тип ссылочного типа называемого делегатом. Делегаты представляют безопасную, с точки зрения типов, версию указателей функций. Они будут рассмотрены позже в этой главе.