Что такое ключевое слово `ref` в C#?

«Что такое ключевое слово `ref` в C#?» — вопрос из категории C# Core, который задают на 36% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Ключевое слово ref в C# используется для передачи аргументов в метод по ссылке. Это означает, что метод работает непосредственно с областью памяти исходной переменной, а не с её копией. Любые изменения значения внутри метода отражаются на исходной переменной.

Базовый пример:

void Increment(ref int number)
{
    number++; // Изменяет значение переменной, переданной извне
}

int x = 5;
Increment(ref x); // ОБЯЗАТЕЛЬНО использовать 'ref' и при вызове
Console.WriteLine(x); // Вывод: 6

Ключевые особенности ref:

  • Инициализация: Переменная, передаваемая как ref-параметр, должна быть явно инициализирована перед вызовом метода.
  • Применение: Может использоваться как с типами значений (int, double, struct), так и со ссылочными типами (class). Для ссылочных типов ref позволяет изменять саму ссылку (указывать на другой объект), а не только содержимое объекта.
    void ReplaceString(ref string str)
    {
    str = "New Value"; // Меняет ссылку в вызывающем коде
    }
    string s = "Old Value";
    ReplaceString(ref s);
    Console.WriteLine(s); // Вывод: New Value
  • Возвращаемые ссылочные значения (C# 7.0+): ref можно использовать для возврата ссылки на элемент массива или поля структуры.
    
    ref int FindValue(int[] numbers, int target)
    {
    for (int i = 0; i < numbers.Length; i++)
        if (numbers[i] == target)
            return ref numbers[i]; // Возвращаем ссылку на элемент массива
    throw new InvalidOperationException("Value not found");
    }

int[] arr = { 1, 2, 3 }; ref int item = ref FindValue(arr, 2); item = 100; // Меняет arr[1] на 100



**Когда использовать `ref`:**
1.  **Изменение входных параметров:** Когда метод должен модифицировать значение исходной переменной.
2.  **Избегание копирования больших структур:** Для повышения производительности при передаче больших `struct` (например, `Matrix`, `Rect`).
3.  **Работа с памятью на низком уровне:** В связке с `unsafe` кодом и указателями.

**Отличие от `out` и `in`:**
*   `out`: Требует, чтобы метод присвоил значение параметру перед возвратом. Переменная может быть не инициализирована до вызова.
*   `in`: Параметр передаётся по ссылке, но метод не может его изменить (предназначен для передачи больших структур только для чтения).