0Pricing
C# Academy · Урок

Перегрузка арифметических операторов

Определяйте +, -, * для собственных типов.

«Перегрузка арифметических операторов» — бесплатный урок C# Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.

Зачем перегружать арифметические операторы

Перегрузка операторов позволяет использовать в собственных типах привычные символы, такие как +, - и *. Для типов, подобных значениям, например векторов, денег или комплексных чисел, это делает код похожим на математические формулы, а не на вызовы методов.

Синтаксис оператора +

Перегруженный оператор объявляется как метод public static, имя которого состоит из слова operator и следующего за ним символа. По крайней мере один параметр должен иметь тип содержащего метода типа.

using System;

public struct Vector2
{
    public int X, Y;
    public Vector2(int x, int y) { X = x; Y = y; }

    public static Vector2 operator +(Vector2 a, Vector2 b)
        => new Vector2(a.X + b.X, a.Y + b.Y);

    public override string ToString() => "(" + X + ", " + Y + ")";
}

public class Program
{
    public static void Main()
    {
        var sum = new Vector2(1, 2) + new Vector2(3, 4);
        Console.WriteLine(sum);
    }
}

Вычитание и умножение

Вы можете перегрузить несколько операторов для одного типа. Здесь мы добавляем к нашему вектору вычитание и умножение на скаляр.

using System;

public struct Vector2
{
    public int X, Y;
    public Vector2(int x, int y) { X = x; Y = y; }

    public static Vector2 operator -(Vector2 a, Vector2 b)
        => new Vector2(a.X - b.X, a.Y - b.Y);
    public static Vector2 operator *(Vector2 a, int k)
        => new Vector2(a.X * k, a.Y * k);

    public override string ToString() => "(" + X + ", " + Y + ")";
}

public class Program
{
    public static void Main()
    {
        var v = new Vector2(5, 6) - new Vector2(2, 1);
        Console.WriteLine(v);
        Console.WriteLine(new Vector2(2, 3) * 4);
    }
}

Операторы всегда статические

Перегруженные операторы должны быть static. Они не работают со скрытым this; вместо этого операнды передаются как явные параметры. Компилятор сопоставляет символ с методом.

using System;

public struct Money
{
    public decimal Amount;
    public Money(decimal amount) { Amount = amount; }

    public static Money operator +(Money a, Money b)
        => new Money(a.Amount + b.Amount);

    public override string ToString() => "$" + Amount;
}

public class Program
{
    public static void Main()
    {
        var total = new Money(10.5m) + new Money(4.25m);
        Console.WriteLine(total);
    }
}

Унарные операторы

Унарные операторы, такие как отрицание, принимают один операнд. Перегрузите operator - с одним параметром, чтобы поддержать выражения вроде -v.

using System;

public struct Vector2
{
    public int X, Y;
    public Vector2(int x, int y) { X = x; Y = y; }

    public static Vector2 operator -(Vector2 a)
        => new Vector2(-a.X, -a.Y);

    public override string ToString() => "(" + X + ", " + Y + ")";
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(-new Vector2(3, -7));
    }
}

Операнды разных типов

Один из операндов может иметь другой тип. Реализация обоих порядков позволяет пользователям естественно записывать выражение в любом варианте.

using System;

public struct Temperature
{
    public double Celsius;
    public Temperature(double c) { Celsius = c; }

    public static Temperature operator +(Temperature t, double delta)
        => new Temperature(t.Celsius + delta);
    public static Temperature operator +(double delta, Temperature t)
        => new Temperature(t.Celsius + delta);

    public override string ToString() => Celsius + "C";
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new Temperature(20) + 5);
        Console.WriteLine(5 + new Temperature(20));
    }
}

Возврат новых значений

Арифметические операторы не должны изменять свои операнды. Они вычисляют и возвращают новое значение, оставляя исходные значения неизменными, как это делает int.

using System;

public struct Vector2
{
    public int X, Y;
    public Vector2(int x, int y) { X = x; Y = y; }
    public static Vector2 operator +(Vector2 a, Vector2 b)
        => new Vector2(a.X + b.X, a.Y + b.Y);
    public override string ToString() => "(" + X + ", " + Y + ")";
}

public class Program
{
    public static void Main()
    {
        var a = new Vector2(1, 1);
        var b = new Vector2(2, 2);
        var c = a + b;
        Console.WriteLine("a unchanged: " + a);
        Console.WriteLine("result: " + c);
    }
}

Цепочки арифметических операций

Поскольку каждый оператор возвращает тот же тип, выражения естественно объединяются в цепочки и соблюдают обычные правила приоритета и ассоциативности.

using System;

public struct Vector2
{
    public int X, Y;
    public Vector2(int x, int y) { X = x; Y = y; }
    public static Vector2 operator +(Vector2 a, Vector2 b) => new Vector2(a.X + b.X, a.Y + b.Y);
    public static Vector2 operator *(Vector2 a, int k) => new Vector2(a.X * k, a.Y * k);
    public override string ToString() => "(" + X + ", " + Y + ")";
}

public class Program
{
    public static void Main()
    {
        var result = new Vector2(1, 1) + new Vector2(2, 2) * 3;
        Console.WriteLine(result);
    }
}

Операторы в классах

Перегрузка операторов работает и в классах, а не только в структурах. Учитывайте значение null: операнд класса может быть равен null, поэтому при необходимости добавьте проверку.

using System;

public class Complex
{
    public double Re, Im;
    public Complex(double re, double im) { Re = re; Im = im; }
    public static Complex operator +(Complex a, Complex b)
        => new Complex(a.Re + b.Re, a.Im + b.Im);
    public override string ToString() => Re + " + " + Im + "i";
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new Complex(1, 2) + new Complex(3, 4));
    }
}

Полноценный числовой тип

Здесь показан небольшой тип дроби со сложением. Он демонстрирует, как перегрузка превращает предметный тип в объект, который ведёт себя подобно встроенному числу.

using System;

public struct Fraction
{
    public int Num, Den;
    public Fraction(int num, int den) { Num = num; Den = den; }

    public static Fraction operator +(Fraction a, Fraction b)
        => new Fraction(a.Num * b.Den + b.Num * a.Den, a.Den * b.Den);

    public override string ToString() => Num + "/" + Den;
}

public class Program
{
    public static void Main()
    {
        Console.WriteLine(new Fraction(1, 2) + new Fraction(1, 3));
    }
}

Попробуйте сами

Добавьте в структуру, представляющую деньги, сложение и умножение на скаляр, а затем составьте выражение, которое выглядит как обычная арифметика.

using System;

public struct Money
{
    public decimal Amount;
    public Money(decimal a) { Amount = a; }
    public static Money operator +(Money a, Money b) => new Money(a.Amount + b.Amount);
    public static Money operator *(Money a, int qty) => new Money(a.Amount * qty);
    public override string ToString() => "$" + Amount;
}

public class Program
{
    public static void Main()
    {
        var price = new Money(4.50m);
        var total = price * 3 + new Money(2.00m);
        Console.WriteLine(total);
    }
}

Быстрая проверка

Вспомните сигнатуру перегруженного оператора.

Итоги

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

  • Объявляйте их с помощью public static и ключевого слова operator.
  • По крайней мере один параметр должен иметь тип содержащего типа.
  • Возвращайте новое значение и никогда не изменяйте операнды.
  • Для арифметики с разными типами предусмотрите оба порядка операндов.

Часто задаваемые вопросы

Урок «Перегрузка арифметических операторов» бесплатный?

Да — полный текст урока «Перегрузка арифметических операторов» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.

Чему я научусь в уроке «Перегрузка арифметических операторов»?

Определяйте +, -, * для собственных типов. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C# Academy?

Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Перегрузка арифметических операторов»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C# Academy?

Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Перегрузка арифметических операторов
  2. Перегрузка операторов сравнения
  3. Пользовательские преобразования
  4. Рекомендации по перегрузке операторов
← Назад к C# Academy