Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Уроки С#.docx
Скачиваний:
2
Добавлен:
01.07.2025
Размер:
346 Кб
Скачать
☆

Ссылочные типы и типы значений в Си-шарп. Ключевые слова ref и out

В языке Си-шарп все типы данных делятся на две категории – ссылочные типы, и типы значений. Они отличаются способом хранения в памяти, производительностью и другим. В этом уроке мы поговорим, что это всё означает, а также о передаче параметров в метод по ссылке (ключевые слова ref и out). Типы значений Эту категорию также называют структурными типами. Типы значений хранятся в стеке. Стек - это область памяти, которая используется для передачи параметров в методы и хранения определенных в пределах методов локальных переменных. Данные переменной типа значения хранятся в самой переменной.  К типам значений относятся:  - Целочисленные типы (byte, sbyte, char, short, ushort, int, uint, long, ulong);  - Типы с плавающей запятой (float, double); - Тип decimal; - Тип bool; - Пользовательские структуры (struct); - Перечисления (enum). Код ниже показывает, что при присвоении значения одной переменной значимого типа другой, дальнейшее изменение одной из переменных не влияет на другую. Так потому, что хранение данных значимого типа происходит в самой переменной:

static void Main(string[] args) {    int a = 1;    int b = 2;    b = a;    a = 3;    Console.WriteLine(a); // 3     Console.WriteLine(b); // 1  }

Ссылочные типы Переменная ссылочного типа содержит не данные, а ссылку на них. Сами данные в этом случае уже хранятся в куче. Куча - это область памяти, в которой размещаются управляемые объекты, и работает сборщик мусора. Сборщик мусора освобождает все ресурсы и объекты, которые уже не нужны.  К ссылочным типам относятся: - Классы (class); - Интерфейсы (interface); - Делегаты (delegate); - Тип object; - Тип string.  В коде ниже был создан простой класс, в котором есть одно поле типа int. Дальше была проделана такая же процедура, как и в случае выше, только результат уже другой. После присвоения одного объекта другому, они стали указывать на одну и ту же область памяти (меняем b – меняется и a):

class Test {    public int x; } class Program {     static void Main(string[] args)    {      Test a = new Test();      Test b = new Test();      a.x = 1;       b.x = 2;      b = a; // присвоение ссылки      b.x = 3;       Console.WriteLine(a.x); // 3      Console.WriteLine(b.x); // 3 } }

Передача параметров в метод по ссылке. Операторы ref и out В C# значения переменных по-умолчанию передаются по значению (в метод передается локальная копия параметра, который используется при вызове). Это означает, что мы не можем внутри метода изменить параметр из вне:

public static void ChangeValue(object a) {    a = 2; } static void Main(string[] args) {    int a = 1;    ChangeValue(a);    Console.WriteLine(a); // 1    Console.ReadLine(); }

Чтобы передавать параметры по ссылке, и иметь возможность влиять на внешнюю переменную, используются ключевые слова ref и out. Ключевое слово ref Чтобы использовать ref, это ключевое слово стоит указать перед типом параметра в методе, и перед параметром при вызове метода:

public static void ChangeValue(ref int a) {    a = 2; } static void Main(string[] args) {    int a = 1;    ChangeValue(ref a);    Console.WriteLine(a); // 2    Console.ReadLine(); }

В этом примере мы изменили значение внешней переменной внутри метода. Особенностью ref является то, что переменная, которую мы передаем в метод, обязательно должна быть проинициализирована значением, иначе компилятор выдаст ошибку «Use of unassigned local variable 'a'». Это является главным отличием ref от out.  Ключевое слово out Out используется точно таким же образом как и ref, за исключением того, что параметр не обязан быть проинициализирован перед передачей, но при этом в методе переданному параметру обязательно должно быть присвоено новое значение:

public static void ChangeValue(out int a) {    a = 2; } static void Main(string[] args) {    int a;    ChangeValue(out a);    Console.WriteLine(a); // 2    Console.ReadLine(); }

Если не присвоить новое значение параметру out, мы получим ошибку «The out parameter 'a' must be assigned to before control leaves the current method» Производительность Учитывая тот факт, что по умолчанию в метод передаются параметры по значению и создаются их копии в стеке, при использовании сложных типов данных (пользовательские структуры), или если метод вызывается много раз, это плохо скажется на производительности. В таком случае также стоит использовать ключевые слова ref и out. Если говорить в целом о ссылочных типах и типах значений, то производительность приложения упадет, если использовать только ссылочные типы. На создание переменной ссылочного типа в куче выделяется память под данные, а в стеке под ссылку на эти данные. Для типов значений память выделяется только в стеке. Время на размещение данных в стеке меньше, чем в куче, это также идет в плюс типам значений в плане производительности.