Типы (руководство по программированию в C#)
Обновлен: Июль 2008
Типы, переменные и значения
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,
}
Константа Create имеет значение 2. Однако имя намного более значимо для пользователей, читающих исходный код, и по этой причине лучше использовать перечисления вместо литеральных чисел констант.
Все перечисления наследуются от 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#)
Универсальные шаблоны (Руководство по программированию на C#)
Спецификация языка C#
Дополнительные сведения о типах CLR см. в следующих разделах документа Спецификация языка C#:
1.3 Типы и переменные
3.8 Имена пространств имен и типов
4.1 Типы значений
4.2 Ссылочные типы
4.3 Упаковка-преобразование и распаковка-преобразование
См. также
Основные понятия
Руководство по программированию в C#
Преобразование типов XML-данных
Ссылки
Сравнение типов данных в разных языках
Таблица целых типов (Справочник по C#)
Другие ресурсы
Журнал изменений
Дата |
Журнал |
Причина |
---|---|---|
Июль 2008 |
Добавлены вводные сведения и сведения об объявлениях типов, о системе общих типов CTS, типах значений и ссылочных типах, литералах и универсальных типах. |
Улучшение информации. |