C#7 – Ссылочные значения

C# logoС# 7 разрешает использовать ключевое слово ref не только для параметров, но так же для возвращаемых значений и локальных переменных. Это позволяет передавать ссылки на структуры вместо самих структур.

Синтаксис записи:

public ref TReturn MethodName(int index)
{
    ...
    return ref x;
}
 
// Примеры вызовов
ref TReturn val1 = ref MethodName();
TReturn val2 = MethodName();

Ключевое слово ref используется при указании типа возвращаемого значения при объявлении метода. Так же оно присутствует при непосредственном возврате ссылочного значения.

Созданный метод может быть использован 2 способами:

  • С указанием ref: результатом будет ссылочное значение на результат метода.
  • Без указания ref: результатом будет копия структуры, как при обычном вызове.

При этом существует ряд ограничений:

  • Используя ref можно вернуть только значения, которые безопасны к возврату:
    • были изначально переданы в метод
    • или ссылаются на поля самого объекта
  • Локальные ссылочные переменные не изменяемые (не стоит путать их с ссылками в C++).

Например, нельзя вернуть локальную переменную как ссылочное значение:

public int Get()
{
    int value = 42;
    return ref value; // Ошибка
}

В качестве примера создадим очень упрощенный пул объектов: зарезервируем массив структур, которые можно будет повторно использовать. В реальном приложении это позволит избежать их частого создания и удаления. Это, в свою очередь, уменьшит нагрузку на сборщик мусора и возможные из-за него паузы в работе.

public class Pool<TStruct>
{
    private readonly TStruct[] _pool;

    public Pool(int maxPoolSize)
    {
        this._pool = new TStruct[maxPoolSize];
    }

    public ref TStruct Get(int index)
    {
        return ref this._pool[index];
    }
}

Вариант его использования:

public struct DemoStruct
{
    public int Value;
}
 
class Program
{
    static void Main(string[] args)
    {
        var pool = new Pool<DemoStruct>(maxPoolSize: 1024);

        ref DemoStruct struct1 = ref pool.Get(24);
        ref DemoStruct struct2 = ref pool.Get(24);
        Console.WriteLine($"ref struct1 = {struct1.Value}");
        struct2.Value = 42;
        Console.WriteLine($"ref struct1 = {struct1.Value}"); // == 42

        DemoStruct struct3 = pool.Get(24);
        Console.WriteLine($"struct3 = {struct3.Value}"); // == 42

        struct3.Value = 1;
        Console.WriteLine($"ref struct1 = {struct1.Value}"); // == 42
        Console.WriteLine($"ref struct2 = {struct2.Value}"); // == 42
        Console.WriteLine($"struct3 = {struct3.Value}"); // == 1

        Console.ReadKey();     }
}

Обратите внимание, что переменные struct1 и struct2 ссылаются на одну и ту же структуру. А struct3 является самостоятельной копией.

Добавить комментарий