Ескертпе
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Жүйеге кіруді немесе каталогтарды өзгертуді байқап көруге болады.
Бұл бетке кіру үшін қатынас шегін айқындау қажет. Каталогтарды өзгертуді байқап көруге болады.
C# — это строго типизированный язык. Каждая переменная и константа имеют тип, как и каждое выражение, которое оценивает значение. C# в основном использует систему нормативных типов.
Система нормативных типов использует имена для идентификации каждого типа. В C# struct, class и interface типы, включая record типы, определяются на основе их имени. Каждое объявление метода задает имя, тип и квалитет (значение, ссылку или выходной параметр) для каждого параметра и возвращаемого значения. Библиотека классов .NET определяет встроенные числовые типы и сложные типы, представляющие широкий спектр конструкций. Эти конструкции включают файловую систему, сетевые подключения, коллекции и массивы объектов и даты. Типичная программа C# использует типы из библиотеки классов и определяемых пользователем типов, которые моделиируют основные понятия, относящиеся к предмету проблемы программы.
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# предоставляет стандартный набор встроенных типов. Эти типы представляют целые числа, значения с плавающей запятой, логические выражения, текстовые символы, десятичные значения и другие типы данных. Язык также включает встроенные string и object типы. Эти типы можно использовать в любой программе C#. Полный список встроенных типов см. в разделе "Встроенные типы".
Пользовательские типы
Создайте структурные типы с помощью кортежей для хранения связанных элементов данных. Эти типы предоставляют структуру, содержащую несколько элементов. Кортежи имеют ограниченное поведение. Это контейнер для значений. Это простейшие типы, которые можно создать. Позже вы можете решить, что вам нужно поведение. В этом случае можно преобразовать кортеж в struct или class.
Используйте конструкции struct, class, interface, enum и record для создания собственных пользовательских типов. Сама библиотека классов .NET — это коллекция пользовательских типов, которые можно использовать в собственных приложениях. По умолчанию наиболее часто используемые типы в библиотеке классов доступны в любой программе C#. Вы делаете другие типы доступными путем явного добавления ссылки на пакет, который предоставляет их. После того как компилятор имеет ссылку на пакет, можно объявить переменные и константы типов, объявленных в сборках этого пакета в исходном коде.
Одним из первых решений, которые вы принимаете при определении типа, является определение конструкции, используемой для вашего типа. Следующий список помогает принять это первоначальное решение. Некоторые варианты перекрываются. В большинстве случаев более одного варианта является разумным выбором.
- Если тип данных не является частью домена приложения и не включает поведение, используйте структурный тип.
- Если размер хранилища данных мал, не более 64 байтов, выберите
structилиrecord struct. - Если тип неизменяем или требуется неразрушительная мутация, выберите
structилиrecord struct. - Если тип должен иметь семантику значений для равенства, выберите или
record classrecord struct. - Если тип предназначен в основном для хранения данных, при минимальном поведении выберите
record classилиrecord struct. - Если тип является частью иерархии наследования, выберите
record classилиclass. - Если тип использует полиморфизм, выберите тип
class. - Если основное назначение — поведение, выберите
class.
Вы также можете выбрать interface для моделирования контракта: поведения, описанного членами, которое может быть реализовано несвязанными типами. Интерфейсы являются абстрактными и объявляют элементы, которые должны быть реализованы всеми class или struct типами, наследуемыми от этого интерфейса.
Общая система типов
Система общего типа поддерживает принцип наследования. Типы могут быть производными от других типов, называемых базовыми типами. Производный тип наследует (с некоторыми ограничениями) методы, свойства и другие члены базового типа. Базовый тип, в свою очередь, может быть производным от другого типа, в этом случае производный тип наследует члены обоих базовых типов в иерархии наследования.
Все типы, включая встроенные числовые типы, такие как System.Int32 (ключевое слово C#: int), в конечном счете являются производными от одного базового типа, который является System.Object (ключевое слово C#: object). Эта унифицированная иерархия типов называется common Type System (CTS). Дополнительные сведения о наследовании в C#см. в разделе "Наследование".
Каждый тип в CTS определяется как тип значения или ссылочный тип. К этим типам относятся все пользовательские типы в библиотеке классов .NET, а также собственные определяемые пользователем типы:
- Типы, которые вы определяете, используя ключевые слова
structилиrecord struct, являются типами значений. Все встроенные числовые типыstructsпредставлены ниже. - Типы, которые определяются с помощью
classrecord classключевых слов,recordявляются ссылочными типами.
Ссылочные типы и типы значений имеют разные правила во время компиляции и другое поведение во время выполнения.
Замечание
Наиболее часто используемые типы организованы в System пространстве имен. Однако пространство имен, в котором содержится тип, не имеет отношения к типу значений или ссылочным типам.
Классы и структуры являются двумя основными конструкциями системы общего типа в .NET. Каждая конструкция по сути представляет собой структуру данных, которая инкапсулирует набор данных и поведения, принадлежащих вместе как логическая единица. Данные и поведение являются членами класса, структуры или записи. В состав входят его методы, свойства, события и т. д., как указано далее в этой статье.
Класс, структура или запись — это как чертеж, который используется для создания экземпляров или объектов во время выполнения программы. Если вы определяете класс, структуру или запись с именем Person, Person является именем типа. Если вы объявляете и инициализируете переменную p типа Person, p считается объектом или экземпляром Person. Можно создать несколько экземпляров одного Person типа, и каждый экземпляр может иметь разные значения в его свойствах и полях.
Класс является ссылочным типом. При создании объекта типа переменная, которой назначается объект, содержит только ссылку на эту память. При назначении ссылки на объект новой переменной новая переменная ссылается на исходный объект. Изменения, внесенные через одну переменную, отражаются в другой переменной, так как они ссылаются на одни и те же данные.
Структура — это тип значения. При создании структуры переменная, которой вы назначаете структуру, содержит фактические данные структуры. При назначении структуры новой переменной она копируется. Поэтому новая переменная и исходная переменная содержат две отдельные копии одинаковых данных. Изменения, внесенные в одну копию, не влияют на другую копию.
Типы записей могут быть ссылочными типами (record class) или типами значений (record struct). Типы записей содержат методы, поддерживающие равенство значений.
Как правило, используйте классы для моделирования более сложного поведения. Классы обычно хранят данные, которые можно изменить после создания объекта класса. Структуры лучше всего подходят для небольших структур данных. Структуры обычно хранят данные, которые не изменяются после создания структуры. Типы записей — это структуры данных с дополнительными синтезируемыми элементами компилятора. Записи обычно хранят данные, которые не изменяются после создания объекта.
Типы значений
Типы значений происходят от System.ValueType, который, в свою очередь, происходит от System.Object. Типы, производные от System.ValueType, имеют особое поведение в среде CLR. Переменные типа значений напрямую содержат их значения. Память для структуры выделяется непосредственно в том контексте, где объявлена переменная. Можно объявить record struct типы значений и включить синтезированные элементы для записей.
Существуют две категории типов значений: struct и enum.
Встроенные числовые типы — это структуры, и у них есть поля и методы, к которым можно получить доступ:
// constant field on type byte.
byte b = byte.MaxValue;
Но вы объявляете и присваиваете им значения, как если бы они простые не агрегатные типы:
byte num = 0xA;
int i = 5;
char c = 'Z';
Типы значений запечатаны. Вы не можете наследить тип от любого типа значения, например System.Int32. Невозможно определить структуру, наследуемую от любого определяемого пользователем класса или структуры, так как структуру можно наследовать только от System.ValueType. Однако структура может реализовать один или несколько интерфейсов. Вы можете привести тип структуры к любому типу интерфейса, который он реализует. Этот приведение вызывает операцию упаковки, которая упаковывает структуру в объект ссылочного типа в управляемой куче. Операции упаковки происходят при передаче значения типа методу, принимающему System.Object или любой тип интерфейса в качестве входного параметра. Дополнительные сведения см. в разделе Упаковка и Распаковка.
Используйте ключевое слово struct для создания собственных настраиваемых типов значений. Как правило, структуру используют в качестве контейнера для небольшого набора связанных переменных, как показано в следующем примере:
public struct Coords(int x, int y)
{
public int X { get; init; } = x;
public int Y { get; init; } = y;
}
Дополнительные сведения о структурах см. в разделе " Типы структур". Дополнительные сведения о типах значений см. в разделе "Типы значений".
Другая категория типов значений — enum. Перечисление определяет набор именованных целочисленных констант. Например, перечисление System.IO.FileMode в библиотеке классов .NET содержит набор именованных целых чисел констант, указывающих способ открытия файла. Он определен, как показано в следующем примере:
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. Все правила, применяемые к структурам, также применяются к перечислениям. Дополнительные сведения о перечислениях см. в разделе Типы перечисления.
Типы ссылок
Тип, который определяется как class, record class, record, delegate, массив или interface, является reference type.
Когда вы объявляете переменную типа reference type, она содержит значение null до тех пор, пока вы не назначите ей экземпляр этого типа или не создадите его с помощью оператора new. В следующем примере показано создание и назначение класса:
MyClass myClass = new();
MyClass myClass2 = myClass;
Вы не можете напрямую создать экземпляр с interface помощью new оператора. Вместо этого создайте и назначьте экземпляр класса, реализующего интерфейс. Рассмотрим следующий пример:
MyClass myClass = new();
// Declare and assign using an existing value.
IMyInterface myInterface = myClass;
// Or create and assign a value in a single statement.
IMyInterface myInterface2 = 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#компилятор назначает тип литеральным значениям. Можно указать, как следует вводить числовый литерал, добавив букву в конец числа. Например, чтобы указать, что значение 4.56 должно рассматриваться как floatзначение, добавьте "f" или "F" после числа: 4.56f Если вы не добавляете букву, компилятор вводит тип для литерала. Дополнительные сведения о типах, которые можно указать суффиксами букв, см. в разделе " Целочисленные числовые типы " и числовые типы с плавающей запятой.
Поскольку литералы вводимы, и все типы в конечном счете являются производными от 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);
Универсальные типы
Объявите тип с одним или несколькими параметрами типа , которые служат заполнителями для фактического типа ( конкретного типа). Клиентский код предоставляет конкретный тип при создании экземпляра типа. Эти типы называются универсальными типами. Например, тип System.Collections.Generic.List<T> .NET имеет один параметр типа, который по соглашению называется T. При создании экземпляра типа укажите тип объектов, содержащихся в списке, например string:
List<string> stringList = new List<string>();
stringList.Add("String example");
// compile time error adding a type other than a string:
stringList.Add(4);
Использование параметра типа позволяет повторно использовать один и тот же класс для хранения любого типа элемента, не преобразовывая каждый элемент в объект. Универсальные классы коллекций являются строго типизированными коллекциями , так как компилятор знает конкретный тип элементов коллекции и может вызвать ошибку во время компиляции, если, например, вы пытаетесь добавить целое число в stringList объект в предыдущем примере. Дополнительные сведения см. в разделе «Обобщения».
Кортежи и анонимные типы
Создание типа для простых наборов связанных значений может оказаться неудобным, если вы не планируете хранить или передавать эти значения с помощью общедоступных API. Для этого можно создавать кортежи или анонимные типы . Дополнительные сведения см. в кортежах и анонимных типах.
Типы значений, допускающие значение NULL
Обычные типы значений не могут иметь значение null. Однако можно создать типы значений, допускающие NULL, добавив ? к типу. Например, int? — это тип int, который также может иметь значение null. Типы значений, допускающие значение NULL, — это экземпляры универсального типа System.Nullable<T>структуры. Типы значений, допускающие NULL, особенно полезны при передаче данных в базы данных и из них, где числовые значения могут быть null. Дополнительные сведения см. в разделе "Типы значений, допускающих значение NULL".
Объявления неявного типа
Неявно введите локальную переменную (но не члены класса) с помощью ключевого var слова. Переменная по-прежнему получает тип во время компиляции, но компилятор предоставляет тип. Дополнительные сведения см. в разделе Неявно типизированные локальные переменные.
Тип времени компиляции и тип времени выполнения
Переменная может иметь разные типы времени компиляции и времени выполнения. Тип времени компиляции — это объявленный или выводемый тип переменной в исходном коде. Тип времени выполнения — это тип экземпляра, на который ссылается эта переменная. Часто эти два типа одинаковы, как в следующем примере:
string message = "This is a string of characters";
В других случаях тип времени компиляции отличается, как показано в следующих двух примерах:
object anotherMessage = "This is another string of characters";
IEnumerable<char> someCharacters = "abcdefghijklmnopqrstuvwxyz";
В обоих предыдущих примерах тип времени выполнения является типом string. Тип времени компиляции находится object в первой строке и IEnumerable<char> во второй.
Если для переменной отличаются два типа, важно понимать, когда применяется тип времени компиляции и тип времени выполнения. Тип времени компиляции определяет все действия компилятора. Эти действия компилятора включают разрешение вызовов методов, разрешение перегрузки и доступные неявные и явные приведения. Тип времени выполнения определяет все действия, которые выполняются во время выполнения. Эти действия во время выполнения включают отправку вызовов виртуальных методов, оценки is и switch выражений и других API тестирования типов. Чтобы лучше понять, как ваш код взаимодействует с типами, распознайте, какое действие относится к какому типу.
Связанные разделы
Дополнительные сведения см. в следующих статьях:
Спецификация языка C#
Дополнительные сведения см. в спецификации языка C#. Спецификация языка является авторитетным источником синтаксиса и использования языка C#.