Типы (Руководство по программированию на C#)
Типы, переменные и значения
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#: объектом).Эта унифицированная иерархия типов называется Система общих типов 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 struct CoOrds
{
public int x, y;
public CoOrds(int p1, int p2)
{
x = p1;
y = p2;
}
}
Дополнительные сведения о структурах см. в разделе Структуры (Руководство по программированию на C#).Дополнительные сведения о типах значений в .NET Framework см. в разделе Система общих типов CTS.
Другой категорией типов значений является перечисление.Перечисление определяет набор именованных интегральных констант.Например, перечисление System.IO.FileMode в библиотеке классов платформы .NET Framework содержит набор именованных констант целого типа, которые задают, как должен быть открыт файл.Это определено, как показано в следующем примере:
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, as shown in the following example:.
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#).
Связанные разделы
Дополнительные сведения см. в следующих разделах.
Приведение и преобразование типов (Руководство по программированию на C#)
Упаковка-преобразование и распаковка-преобразование (Руководство по программированию на C#)
Использование типа dynamic (Руководство по программированию на C#)
Универсальные шаблоны (Руководство по программированию на C#)
Спецификация языка C#
Дополнительные сведения см в Спецификация языка C#. Спецификация языка является предписывающим источником информации о синтаксисе и использовании языка C#.
См. также
Ссылки
Сравнение типов данных в разных языках
Таблица целых типов (Справочник по C#)
Основные понятия
Руководство по программированию на C#