Полное руководство по языку программирования С# 14 и платформе .NET 10 / Дополнительные возможности ООП в C#

ref-структуры

Начальный ⏱ 4 мин чтения 👁 2 🕓 обновлено 11.08.2026

Структуры с модификатором ref (далее ref-структуры) представляют такие типы, которые размещаются в стеке. В то время как обычные структуры могут быть упакованы в ссылочные типы и помещены в управляемой куче (хипе), ref-структуры размещаются только в стеке. Это делает ref-структуры удобным инструментом в различных сценариях, где нужна высокая производительность. Среди встроенных типов .NET к ref-структурам, в частности, относят ReadOnlySpan<char>.

Основные особенности ref-структур:

  • Память под ref-структуры выделяется только в стеке, в кучу они не перемещаются

  • ref-структуры не могут быть упакованы в ссылочные типы (иначе это потребовало бы размещение такиз структур в куче). Поэтому их нельзя преобразовать в ссылочный тип, например, в тип интерфейса, даже если они реализуют этот интерфейс

  • ref-структуры характеризуются большей безопасностью, поскольку размещаются в стеке и соответственно не страдают от утечек памяти и других проблем, связанных с управляемой кучей.

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

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

В то же время ref-структуры имеют ряд ограничений:

  • ref-структура не может представлять элемент массива

  • ref-структура не может представлять поле класса или обычной структуры

  • ref-структура не может быть упакована в System.ValueType или System.Object

  • ref-структура не может быть захвачена в лямбда-выражении или локальной функции

Определение ref-структур

ref-структуры определяются как обычные структуры только с модификатором ref. Например:


var tom = new Person("Tom");
Console.WriteLine(tom.Name);

ref struct Person
{
    public Person(string name) => Name = name;
    public string Name {get;}
}

Более практический пример:


int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9};

Sum summator = new Sum(numbers, 1..3);
Console.WriteLine(summator.Get());      // 5

summator = new Sum(numbers, 2..4);
Console.WriteLine(summator.Get());      // 7

ref struct Sum
{
    ReadOnlySpan<int> data;
    
    public Sum(int[] numbers, Range range)
    {
        data = numbers[range];
    }
    public int Get()
    {
        int result = 0;
        foreach(var n in data)
        {
            result += n;
        }
        return result;
    }
}

Здесь структура Sum вычисляет сумму значений коллекции на некотором диапазоне. Диапазон и коллекция в виде массива передаются через конструктор. Диапазон представляет тип Range. В конструкторе получаем нужный диапазон массива в объект типа ReadOnlySpan<int>. Для передачи диапазона в конструкторе применяется выражение 1..3, то есть получаем иапазон из элементов от 1 индекса до 3-го (не включая). В методе Get в цикле вычисляем сумму значений на указанном диапазоне.

Реализация интерфейсов в ref-структурах

До C# 13 ref-структуры не могли реализовывать интерфейсы. Начиная с C# 13, они могут. Однако, в силу ограничений ref-структур, такая структура не может быть преобразован в тип интерфейса. Это преобразование является преобразованием упаковки и может нарушить безопасность ref. ref-структуры также не могут объявлять методы, которые явно реализуют методы интерфейса. Кроме того, ref-структуры должны реализовывать все методы, объявленные в интерфейсе, включая методы с реализацией по умолчанию. Например:



Shape myShape = new();
myShape.Draw();     // Draw some shape

// Draw_Shape(myShape);           // ! Ошибка - Так нельзя
// var shape2 = (IShape)shape; // ! Ошибка - Так нельзя

void Draw_Shape(IShape shape)
{
    shape.Draw();
}

interface IShape
{
    void Draw();
}

ref struct Shape : IShape
{ 
    public void Draw()
    {
        Console.WriteLine("Draw some shape");
    }
}

Обратите внимание, что мы не можем преобразовать значение ref-структуры (в данном случае Shape) к типу интерфейса (IShape):

var shape2 = (IShape)shape

Аналогично мы не можем использовать значение ref-структуры там, где требуется объект интерфейса (так как все равно идет преобразование, только неявно), как в случае с методом Draw_Shape:


// var shape2 = (IShape)shape; // ! Ошибка - Так нельзя

void Draw_Shape(IShape shape)
{
    shape.Draw();
}

Проверь себя

📋 Тест по теме «ref-структуры» Тест по теме

Практические задания

Лёгкое Ручная проверка Заполните пропуск в коде (1)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

var _____ = new Person("Tom");
Console.WriteLine(tom.Name);

ref struct Person
{
    public Person(string name) => Name = name;
    public string Name {get;}
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

int[] numbers = {_____, 2, 3, 4, 5, 6, 7, 8, 9};

Sum summator = new Sum(numbers, 1..3);
Console.WriteLine(summator.Get());      // 5

summator = new Sum(numbers, 2..4);
Console.WriteLine(summator.Get());      // 7

ref struct Sum
{
    ReadOnlySpan<int> data;
    
    public Sum(int[] numbers, Range range)
    {
        data = numbers[range];
    }
    public int Get()
    {
        int result = 0;
        foreach(var n in data)
        {
            result += n;
        }
        return result;
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (3)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

Shape myShape = _____();
myShape.Draw();     // Draw some shape

// Draw_Shape(myShape);           // ! Ошибка - Так нельзя
// var shape2 = (IShape)shape; // ! Ошибка - Так нельзя

void Draw_Shape(IShape shape)
{
    shape.Draw();
}

interface IShape
{
    void Draw();
}

ref struct Shape : IShape
{ 
    public void Draw()
    {
        Console.WriteLine("Draw some shape");
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (4)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

var _____ = (IShape)shape
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (5)
+10 XP

В приведённом ниже фрагменте кода из этого урока пропущено одно слово. Определите его и впишите в поле ответа (без кавычек и пробелов).

// var shape2 = (_____)shape; // ! Ошибка - Так нельзя

void Draw_Shape(IShape shape)
{
    shape.Draw();
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

var _____ = new _____("Tom");
Console.WriteLine(tom.Name);

ref struct Person
{
    public Person(string name) => Name = name;
    public string Name {get;}
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

В этом фрагменте кода урока пропущены два ключевых элемента. Впишите оба пропущенных значения через запятую в порядке появления (например: x, 5).

int[] numbers = {_____, 2, 3, 4, 5, 6, 7, 8, 9};

Sum _____ = new Sum(numbers, 1..3);
Console.WriteLine(summator.Get());      // 5

summator = new Sum(numbers, 2..4);
Console.WriteLine(summator.Get());      // 7

ref struct Sum
{
    ReadOnlySpan<int> data;
    
    public Sum(int[] numbers, Range range)
    {
        data = numbers[range];
    }
    public int Get()
    {
        int result = 0;
        foreach(var n in data)
        {
            result += n;
        }
        return result;
    }
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Расставьте строки кода в правильном порядке
+20 XP

Строки кода из примера этого урока перемешаны. Впишите их в правильном порядке, каждую строку — с новой строки.

Console.WriteLine(tom.Name);
    public string Name {get;}
{
    public Person(string name) => Name = name;
}
ref struct Person
var tom = new Person("Tom");
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «ref-структуры»
+30 XP

Опишите своими словами, что делает приведённый пример кода из урока «ref-структуры», и предложите одно осмысленное изменение (например, другое входное значение или дополнительное условие). Приведите итоговый код.

var tom = new Person("Tom");
Console.WriteLine(tom.Name);

ref struct Person
{
    public Person(string name) => Name = name;
    public string Name {get;}
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «ref-структуры»
+30 XP

Изучите приведённый фрагмент кода и опишите, при каких входных данных или условиях он может сработать не так, как ожидается (граничный случай, отсутствие проверки и т.п.). Предложите исправленный вариант.

int[] numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9};

Sum summator = new Sum(numbers, 1..3);
Console.WriteLine(summator.Get());      // 5

summator = new Sum(numbers, 2..4);
Console.WriteLine(summator.Get());      // 7

ref struct Sum
{
    ReadOnlySpan<int> data;
    
    public Sum(int[] numbers, Range range)
    {
        data = numbers[range];
    }
    public int Get()
    {
        int result = 0;
        foreach(var n in data)
        {
            result += n;
        }
        return result;
    }
}
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «ref-структуры»
+50 XP

Соберите небольшую программу, которая последовательно демонстрирует ключевые идеи урока. Программа должна быть рабочей и содержать минимум два самостоятельно написанных фрагмента, а не просто копию примера из урока.

Войдите, чтобы отправить решение
Домашнее задание Ручная проверка Домашнее задание по теме «ref-структуры»
+25 XP

Напишите собственную небольшую программу, применяющую материал темы «ref-структуры». Используйте не менее одной конструкции, показанной в примерах урока. Прикрепите код решения и краткое описание того, что он делает.

Войдите, чтобы отправить решение

Обсуждение (0)

Войдите, чтобы оставить комментарий.