Типы, переменные и значения

править

C# является строго типизированным языком. Каждая переменная и константа имеет тип, как и каждое выражение, результатом вычисления которого является значение. Каждая сигнатура метода задает тип для каждого входного параметра и для возвращаемого значения. Библиотека классов платформы .NET Framework определяет набор встроенных числовых типов, а также более сложных типов, представляющих широкое разнообразие логических конструкций, например, файловую систему, сетевые подключения, коллекции и массивы объектов и даты. Типичная программа C# использует типы из библиотеки классов, а также пользовательские типы, моделирующие принципы, относящиеся к проблемной области программы.

К сведениям, хранимым в типе, может относиться следующее:

  • Место для хранения, необходимое для переменной типа.
  • Максимальное и минимальное значения, которые могут быть представлены.
  • Содержащиеся члены (методы, поля, события и т. д.).
  • Базовый тип, которому он наследует.
  • Расположение, в котором будет выделена память для переменных во время выполнения.
  • Разрешенные виды операций.

Компилятор использует сведения о типе, чтобы убедиться, что все операции, выполняемые в коде, являются типобезопасными. Например, при объявлении переменной типа int, компилятор позволяет использовать в дополнение переменную и операции вычитания. При попытке выполнить эти же операции в переменной типа bool, компилятор вызовет ошибку, как показано в следующем примере:

int a = 5;             
int b = a + 2; //OK

bool test = true;

// Error. Operator '+' cannot be applied to operands of type 'int' and 'bool'.
int c = a + test;
Примечание
Разработчикам, работающим с C и C++, следует обратить внимание на то, что в C# bool нельзя преобразовать в int.

Компилятор внедряет сведения о типе в исполняемый файл в качестве метаданных. Среда CLR использует эти метаданные во время выполнения для дальнейшего обеспечения безопасности типа при выделении и освобождении памяти. Задание типов в объявлениях переменных При объявлении переменной или константы в программе необходимо либо задать ее тип, либо использовать ключевое слово var, чтобы дать возможность компилятору определить его. В следующем примере показаны некоторые объявления переменных, использующие встроенные числовые типы и сложные пользовательские типы:

// Declaration only:
float temperature;
string name;
MyClass myClass;

// Declaration with initializers (four examples):
char firstLetter = 'C';
var limit = 3;
int[] source = { 0, 1, 2, 3, 4, 5 };
var query = from item in source
            where item <= limit
            select item;

Типы параметров метода и возвращаемые значения задаются в сигнатуре метода. В следующей сигнатуре показан метод, который требует int в качестве входного аргумента, и который возвращает строку:

public string GetName(int ID)
{
    if (ID < names.Length)
        return names[ID];
    else
        return String.Empty;
}
private string[] names = { "Spencer", "Sally", "Doug" };

После объявления переменной она не может быть повторно объявлена с новым типом, и ей нельзя присвоить значение, несовместимое с ее объявленным типом. Например, нельзя объявить int и затем присвоить ему логическое значение true. Однако значения могут быть преобразованы в другие типы, например, при их присвоении новым переменным или при передаче в качестве аргументов метода. Преобразование типов, которое не приводит к потере данных, автоматически выполняется компилятором. Для преобразования, которое может привести к потере данных, необходимо приведение в исходном коде. Дополнительные сведения см. в разделе Приведение и преобразование типов (Руководство по программированию на C#).

Встроенные типы

править

C# предоставляет стандартный набор встроенных числовых типов для представления целых чисел, значений с плавающей запятой, логических выражений, текстовых символов, десятичных значений и других типов данных. Существуют также встроенные типы string и object. Они доступны для использования в любой программе C#. Дополнительные сведения о встроенных типах см. в разделе Справочные таблицы по типам (Справочник по C#).

Пользовательские типы

править

Конструкции структура, класс, интерфейс и перечисление используются для создания собственных пользовательских типов. Сама библиотека классов платформы .NET Framework является коллекцией пользовательских типов, предоставленной корпорацией Microsoft, которую можно использовать в собственных приложениях. По умолчанию наиболее часто используемые типы в библиотеке классов доступны в любой программе C#. Другие становятся доступными только при явном добавлении ссылки проекта на сборку, в которой они определены. Если компилятор имеет ссылку на сборку, то можно объявить переменные (и константы) типов, объявленных в сборке в исходном коде. Дополнительные сведения см. в разделе Библиотека классов .NET Framework.

Система общих типов CTS

править

Важно понимать две фундаментальные точки о системе типов в .NET Framework:

  • Она поддерживает принцип наследования. Типы могут быть производными от других типов, которые называются базовыми типами. Производный тип наследует (с некоторыми ограничениями) методы, свойства и другие члены базового типа. Базовый тип, в свою очередь, может быть производным от какого-то другого типа, при этом производный тип наследует члены обоих базовых типов в иерархии наследования. Все типы, включая встроенные числовые типы, например, System.Int32 (ключевое слово C#: int), в конечном счете являются производными от одного базового типа, которым является System.Object (ключевое слово C#: object). Эта унифицированная иерархия типов называется Система общих типов CTS (CTS). Дополнительные сведения о наследовании в C# см. в разделе Наследование (Руководство по программированию на C#).
  • Каждый тип в CTS определен либо как тип значения, либо как ссылочный тип. Сюда включены все пользовательские типы в библиотеке классов платформы .NET Framework, а также собственные пользовательские типы. Типы, определяемые с помощью ключевого слова struct, являются типами значений; все встроенные числовые типы являются structs. Типы, определяемые с помощью ключевого слова class, являются ссылочными типами. Правила времени компиляции и поведение времени выполнения ссылочных типов отличается от правил времени компиляции и поведения времени выполнения типов значений.

В следующем примере показана связь между типами значений и ссылочными типами в CTS. Типы значений и ссылочные типы в CTS

Примечание
Можно увидеть, что все наиболее часто используемые типы организованы в пространстве имен System. Однако пространство имен, в котором содержится тип, не имеет отношения к тому, является ли этот тип типом значения или ссылочным типом.

Типы значений

править

Типы значений являются производными от System.ValueType, являющегося производным от System.Object. Типы, производные от System.ValueType, имеют особое поведение в среде CLR. Переменные типа значения напрямую содержат их значения, что означает, что память встроена в контекст, в котором объявлена переменная. Не существует отдельного размещения кучи или служебных данных сборки мусора для переменных типа значения. Существует две категории типов значений: структура и перечисление. Встроенные числовые типы являются структурами, и к их свойствам и методам можно получить доступ.

// Static method on type Byte.
byte b = Byte.MaxValue;

Но значения объявляются и присваиваются им, как если бы они были простыми не статическими типами:

byte num = 0xA;
int i = 5;
char c = 'Z';

Типы значений являются запечатанными, что означает, например, что нельзя произвести тип от System.Int32, и нельзя определить структуру для наследования от любого пользовательского класса или структуры, поскольку структура может наследовать только от System.ValueType. Однако структура может реализовать один или несколько интерфейсов. Можно выполнить приведение типа структуры в тип интерфейса; это приведет к операции упаковки-преобразования для создания программы-оболочки структуры внутри объекта ссылочного типа в управляемой куче. Операции упаковки-преобразования возникают при передаче типа значения методу, принимающему System.Object в качестве входного параметра. Дополнительные сведения см. в разделе Упаковка-преобразование и распаковка-преобразование (Руководство по программированию на C#). Ключевое слово struct используется для создания собственных пользовательских типов значений. Обычно структура используется как контейнер для небольшого набора связанных переменных, как показано в следующем примере:

public enum FileMode
{
    CreateNew = 1,
    Create = 2,
    Open = 3,
    OpenOrCreate = 4,
    Truncate = 5,
    Append = 6,
}

Значение константы System.IO.FileMode.Create равно 2. Однако имя намного более значимо для пользователей, читающих исходный код, и по этой причине лучше использовать перечисления вместо литеральных номеров констант. Дополнительные сведения см. в разделе System.IO.FileMode. Все перечисления наследуются от System.Enum, который наследуется от System.ValueType. Все правила, применимые к структурам, также применяются к перечислениям. Дополнительные сведения о перечислениях см. в разделе Типы перечислений (Руководство по программированию в C#).

Ссылочные типы

править

Тип, определенный как класс, делегат, массив или интерфейс, является ссылочным типом. Во время выполнения при объявлении переменной ссылочного типа переменная содержит значение null до явного создания экземпляра объекта с помощью оператора new или назначения его объекту, который был создан в другом месте, с помощью new, как показано в следующем примере:

MyClass mc = new MyClass();
MyClass mc2 = mc;

Интерфейс должен быть инициализирован вместе с объектом класса, который его реализует. Если MyClass реализует IMyInterface, то создайте экземпляр IMyInterface, как показано в следующем примере:

IMyInterface iface = new MyClass();

При создании объекта память размещается в управляемой куче, и переменная хранит только ссылку на расположение объекта. Для типов в управляемой куче требуются служебные данные и при их размещении, и при их удалении функциональной возможностью автоматического управления памятью среды CLR, также известной как сборка мусора. Однако сборка мусора также в высокой степени оптимизирована, и в большинстве сценариев она не создает проблем с производительностью. Дополнительные сведения о сборке мусора см. в разделе Автоматическое управление памятью. Все массивы являются ссылочными типами, даже если их члены являются типами значений. Массивы являются неявно производными от класса System.Array, но объявляются и используются они с упрощенным синтаксисом, предоставленным C#, как показано в следующем примере:

// Declare and initialize an array of integers.
int[] nums = { 1, 2, 3, 4, 5 };

// Access an instance property of System.Array.
int len = nums.Length;

Ссылочные типы полностью поддерживают наследование. При создании класса можно наследовать от любого другого интерфейса или класса, который не определен как запечатанный, а другие классы могут наследовать от этого класса и переопределять виртуальные методы. Дополнительные сведения о создании собственных классов см. в разделе Классы и структуры (Руководство по программированию в C#). Дополнительные сведения о наследовании и виртуальных методах см. в разделе Наследование (Руководство по программированию на C#).

Типы литеральных значений

править

В C# литеральные значения получают тип от компилятора. Можно задать, как числовой литерал должен быть типизирован, путем добавления буквы в конце номера. Например, чтобы задать, что значение 4,56 должно обрабатываться как число с плавающей запятой, добавьте после номера "f" или "F": 4.56f. При отсутствии добавленной буквы компилятор определит тип для литерала. Дополнительные сведения о том, какие типы могут быть заданы с буквенными суффиксами, см. на страницах справочника для отдельных типов в разделе Типы значений (Справочник по C#).

Поскольку литералы являются типизированными и все типы в конечном счете являются производными от System.Object, можно записать и скомпилировать код, например, следующий:

string s = "The answer is " + 5.ToString();
// Outputs: "The answer is 5"
Console.WriteLine(s);

Type type = 12345.GetType();
// Outputs: "System.Int32"
Console.WriteLine(type);

Универсальные типы

править

Тип может быть объявлен с одним или несколькими параметрами типа, служащими в качестве местозаполнителя для фактического типа (устойчивого типа), который клиентский код предоставит при создании экземпляра типа. Такие типы называются универсальными типами. Например, тип платформы .NET Framework System.Collections.Generic.List<T> имеет один параметр типа, которому в соответствии с соглашением предоставлено имя T. При создании экземпляра типа необходимо задать тип объектов, которые будут содержаться в списке, например, строку: List<string> strings = new List<string>();

Использование параметра типа делает возможным повторное использование этого же класса для хранения любого типа элемента, не преобразовывая каждый элемент в объект. Универсальные классы коллекции называются строго типизированными коллекциями, поскольку компилятор знает определенный тип элементов коллекции и может вызвать ошибку во время компиляции, если, к примеру, будет произведена попытка добавить целое число к объекту strings в предыдущем примере. Дополнительные сведения см. в разделе Универсальные шаблоны (Руководство по программированию на C#).

Неявные типы, анонимные типы и типы, допускающие значение NULL

править

Как уже говорилось ранее, можно неявно типизировать локальную переменную (но не члены класса) с помощью ключевого слова var. Переменная все же получает тип во время компиляции, но тип предоставляется компилятором. Дополнительные сведения см. в разделе Неявно типизированные локальные переменные (Руководство по программированию в C#).

В некоторых случаях неудобно создавать именованный тип для простых наборов связанных значений, которые не будут сохранены или переданы за пределы метода. Для этой цели можно создать анонимные типы. Дополнительные сведения см. в разделе Анонимные типы (Руководство по программированию в C#).

Обычные типы значений не могут иметь значение null. Однако можно создать типы значений, допускающие значение NULL, путем привязки ? после типа. Например, int? является типом int, который также может иметь значение null. В CTS типы, допускающие значения NULL, являются экземплярами универсального типа структуры System.Nullable<T>. Типы, допускающие значение NULL, особенно полезны при передаче данных в базы данных и из них, в которых числовые значение могут быть равны NULL. Дополнительные сведения см. в разделе Типы, допускающие значения NULL (Руководство по программированию на C#).