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

Инкремент и декремент

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

Начиная с версии C# 14 можно для своих типов настраивать поведение составных операторов присваивания таким образом, чтобы цель присваивания изменялась на месте.

При этом язык C# уже в более ранних версиях поддерживал определение составных операторов присваивания операторов для кастомных типов, которые определяет сам разработчик. Например:


Counter counter1 = new Counter { Value = 23 };
Counter counter2 = counter1 + 45;
Console.WriteLine(counter2.Value);  // 23 + 45 = 68

class Counter
{
    static int count;
    public int Id {get;}
    public int Value { get; set; }

    public Counter()
    {
        Id = ++count;
    }
    public static Counter operator +(Counter c, int n)
    {
        return new Counter { Value = c.Value + n };
    }
}

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


Counter counter1 = new Counter { Value = 23 };
counter1 += 45;
Console.WriteLine($"counter1.Value: {counter1.Value}");  // 68

Таким образом, хотя мы явным образом НЕ определяли составной оператор присваивания += для типа Counter, но благодаря оператору сложения + мы можем выполнять сложение с присвоением. И по консольному выводу мы увидим, что все работает правильно. Подобным образом мы можем определять другие операторы без присвоения, и они будут не явно использоваться там, где требуется аналогичный оператор с присваиванием. Например, определить оператор - и выполнять операцию -=.

Однако все таки здесь есть проблема. Так, инструкция

counter1 += 45;

Фактически аналогична следующей:

counter1 = counter1 + 45;

То есть при сложении будет создаваться новый объект Counter. И я не случайно добавил свойство Id, которое и поможет нам это увидеть:


Counter counter1 = new Counter { Value = 23 };

Console.WriteLine($"counter1.Id: {counter1.Id}"); // counter1.Id: 1

counter1 += 45;  // Здесь создается новый объект Counter
Console.WriteLine($"counter1.Value: {counter1.Value}");  // 68

Console.WriteLine($"counter1.Id: {counter1.Id}");   // counter1.Id: 2

Консольный вывод:


counter1.Id: 1
counter1.Value: 68
counter1.Id: 2

Мы видим, что Id у counter1 изменился, то есть конструктор сработал два раза. А если у нас несколько подобных операций:


Counter counter1 = new Counter { Value = 0 };
counter1 += 1;  // 2-й вызов конструктора
counter1 += 1;  // 3-й вызов конструктора
counter1 += 1;  // 4-й вызов конструктора
Console.WriteLine($"counter1.Value: {counter1.Value}");  // 3

Очевидно, что такое поведение не очень эффективно. И чтобы решить подобные проблемы в версию языка C# 14 (.NET 10) и была добавлена возможность отдельно определеить составные операции присваивания, которы вместо создания нового объекта позволяют изменить текущий объект.

Фактически, составной оператор присваивания - это метод объекта, который возвращает void (то есть фактически ничего не возвращает), который принимает один параметр и который имеет специальное имя название оператора. К объявлениям составных операторов присваивания применяются следующие правила:

  • Объявление оператора не должно содержать модификатор static

  • Оператор должен принимать один параметр

  • В качестве возвращаемого типа для оператора применяется void.

  • Имя метода оператора совпадает с названием оператора (+=, -=, *=, /=, %=, &=, |=, ^=, , >>= или >>>=)

Определим оператор += для типа Counter:


Counter counter1 = new Counter { Value = 0 };

Console.WriteLine($"counter1.Id: {counter1.Id}"); // counter1.Id: 1

counter1 += 1;
counter1 += 1;
counter1 += 1;

Console.WriteLine($"counter1.Value: {counter1.Value}");  // 3

Console.WriteLine($"counter1.Id: {counter1.Id}");   // counter1.Id: 1

class Counter
{
    static int count;
    public int Id {get;}
    public int Value { get; set; }

    public Counter()
    {
        Id = ++count;
    }
    public void operator +=(int n)
    {
        Value += n;
    }
}

И по консольному выводы мы увидим, что при работе оператора конструктор не вызывается и новых объектов не создается, изменяется только текущий объект:


counter1.Id: 1
counter1.Value: 3
counter1.Id: 1

Подобным образом можно определить несколько одноименных операторов:


Counter counter1 = new Counter { Value = 0 };

counter1 += 1;
counter1 += new Counter { Value = 5 };

counter1 += 2;

Console.WriteLine(counter1.Value);  // 8

class Counter
{
    public int Value { get; set; }

    public void operator +=(int n)
    {
        Value += n;
    }

    public void operator +=(Counter c)
    {
        Value += c.Value;
    }
}

Операции инкремента и декремента фактически созвучны операциям += и -=, и их также можно определить подобным образом:


Counter counter1 = new Counter { Value = 1 };

counter1++;
Console.WriteLine(counter1.Value);  // 2
++counter1;
Console.WriteLine(counter1.Value);  // 3

class Counter
{
    public int Value { get; set; }

    public void operator ++() => ++Value;
}

Однако в данном случае есть проблема - мы не можем написать следующим образом:


Counter counter2 = ++counter1;
// или так
Counter counter2 = counter1++;

В этом случае мы можем комбинировать два определения оператора - статическое и нестатическое:


Counter counter1 = new Counter { Value = 1 };

Counter counter2 = ++counter1;
Console.WriteLine(counter1.Value);  // 2
Console.WriteLine(counter2.Value);  // 2


class Counter
{
    public int Value { get; set; }

    // два определения оператора
    public static Counter operator ++(Counter c) => new Counter{Value = c.Value++};
    public void operator ++() => ++Value;
}

Производные классы могут переопределять операторы присваивания базовых классов:


var dc = new DoubleCounter { Value = 0 };

dc += 3;
Console.WriteLine(dc.Value);  // 6


class Counter
{
    public int Value { get; set; }
    public virtual void operator +=(int n) => Value += n;
}
class DoubleCounter : Counter
{
    public new int Value { get; set; }
    // переопределяем оператор
    public override void operator +=(int n) => Value += 2*n;
}

Проверь себя

📋 Тест по теме «Инкремент и декремент» Тест по теме

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

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

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

_____ counter1 = new Counter { Value = 23 };
Counter counter2 = counter1 + 45;
Console.WriteLine(counter2.Value);  // 23 + 45 = 68

class Counter
{
    static int count;
    public int Id {get;}
    public int Value { get; set; }

    public Counter()
    {
        Id = ++count;
    }
    public static Counter operator +(Counter c, int n)
    {
        return new Counter { Value = c.Value + n };
    }
}
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (2)
+10 XP

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

Counter _____ = new Counter { Value = 23 };
counter1 += 45;
Console.WriteLine($"counter1.Value: {counter1.Value}");  // 68
Войдите, чтобы отправить решение
Лёгкое Ручная проверка Заполните пропуск в коде (3)
+10 XP

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

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

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

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

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

Counter counter1 = new Counter { _____ = 23 };

Console.WriteLine($"counter1.Id: {counter1.Id}"); // counter1.Id: 1

counter1 += 45;  // Здесь создается новый объект Counter
Console.WriteLine($"counter1.Value: {counter1.Value}");  // 68

Console.WriteLine($"counter1.Id: {counter1.Id}");   // counter1.Id: 2
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (1)
+20 XP

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

_____ counter1 = new Counter { Value = 23 };
Counter counter2 = counter1 + 45;
_____.WriteLine(counter2.Value);  // 23 + 45 = 68

class Counter
{
    static int count;
    public int Id {get;}
    public int Value { get; set; }

    public Counter()
    {
        Id = ++count;
    }
    public static Counter operator +(Counter c, int n)
    {
        return new Counter { Value = c.Value + n };
    }
}
Войдите, чтобы отправить решение
Среднее Ручная проверка Восстановите код по памяти (2)
+20 XP

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

_____ counter1 = new Counter { Value = 23 };
counter1 += _____;
Console.WriteLine($"counter1.Value: {counter1.Value}");  // 68
Войдите, чтобы отправить решение
Среднее Ручная проверка Расставьте строки кода в правильном порядке
+20 XP

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

Console.WriteLine($"counter1.Value: {counter1.Value}");  // 68
counter1 += 45;
Counter counter1 = new Counter { Value = 23 };
Войдите, чтобы отправить решение
Сложное Ручная проверка Объясните и модифицируйте пример из урока «Инкремент и декремент»
+30 XP

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

Counter counter1 = new Counter { Value = 23 };
Counter counter2 = counter1 + 45;
Console.WriteLine(counter2.Value);  // 23 + 45 = 68

class Counter
{
    static int count;
    public int Id {get;}
    public int Value { get; set; }

    public Counter()
    {
        Id = ++count;
    }
    public static Counter operator +(Counter c, int n)
    {
        return new Counter { Value = c.Value + n };
    }
}
Войдите, чтобы отправить решение
Сложное Ручная проверка Найдите потенциальную проблему в коде урока «Инкремент и декремент»
+30 XP

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

Counter counter1 = new Counter { Value = 23 };
counter1 += 45;
Console.WriteLine($"counter1.Value: {counter1.Value}");  // 68
Войдите, чтобы отправить решение
Мини-проект Ручная проверка Мини-проект по теме «Инкремент и декремент»
+50 XP

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

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

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

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

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

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