Рейтинговые книги
Читем онлайн Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 139 140 141 142 143 144 145 146 147 ... 407
внутренние операции С#. Итак, займемся концепцией перегрузки операций.

Понятие перегрузки операций

Как и любой язык программирования, C# имеет заготовленный набор лексем, используемых для выполнения базовых операций над встроенными типами. Например, вы знаете, что операция + может применяться к двум целым числам, чтобы получить большее целое число:

// Операция + с целыми числами.

int a = 100;

int b = 240;

int c = a + b; //  с теперь имеет значение 340

Опять-таки, здесь нет ничего нового, но задумывались ли вы когда-нибудь о том, что одну и ту же операцию + разрешено использовать с большинством встроенных типов данных С#? Скажем, взгляните на следующий код:

// Операция + со строками.

string s1 = "Hello";

string s2 = " world!";

string s3 = s1 + s2;  // s3 теперь имеет значение "Hello World!"

Операция + функционирует специфическим образом на основе типа предоставленных данных (в рассматриваемом случае строкового или целочисленного). Когда операция + применяется к числовым типам, в результате выполняется суммирование операндов, а когда к строковым типам — то конкатенация строк.

Язык C# дает возможность строить специальные классы и структуры, которые также уникально реагируют на один и тот же набор базовых лексем (вроде операции +). Хотя не каждая операция C# может быть перегружена, перегрузку допускают многие операции (табл. 11.1).

Перегрузка бинарных операций

Чтобы проиллюстрировать процесс перегрузки бинарных операций, рассмотрим приведенный ниже простой класс Point, который определен в новом проекте консольного приложения по имени OverloadedOps:

using System;

namespace OverloadedOps

{

  // Простой будничный класс С#.

  public class Point

  {

    public int X {get; set;}

    public int Y {get; set;}

    public Point(int xPos, int yPos)

    {

      X = xPos;

      Y = yPos;

    }

    public override string ToString()

      => $"[{this.X}, {this.Y}]";

  }

}

Рассуждая логически, суммирование объектов Point имеет смысл. Например, сложение двух переменных Point должно давать новый объект Point с просуммированными значениями свойств X и Y. Конечно, полезно также и вычитать один объект Point из другого. В идеале желательно иметь возможность записи примерно такого кода:

using System;

using OverloadedOps;

// Сложение и вычитание двух точек?

Console.WriteLine("***** Fun with Overloaded Operators *****n");

// Создать две точки.

Point ptOne = new Point(100, 100);

Point ptTwo = new Point(40, 40);

Console.WriteLine("ptOne = {0}", ptOne);

Console.WriteLine("ptTwo = {0}", ptTwo);

//  Сложить две точки, чтобы получить большую точку?

Console.WriteLine("ptOne + ptTwo: {0} ", ptOne + ptTwo);

// Вычесть одну точку из другой, чтобы получить меньшую?

  Console.WriteLine("ptOne - ptTwo: {0} ", ptOne - ptTwo);

  Console.ReadLine();

Тем не менее, с существующим видом класса Point вы получите ошибки на этапе компиляции, потому что типу Point не известно, как реагировать на операцию + или -. Для оснащения специального типа способностью уникально реагировать на встроенные операции язык C# предлагает ключевое слово operator, которое может использоваться только в сочетании с ключевым словом static. При перегрузке бинарной операции (такой как + и -) вы чаще всего будете передавать два аргумента того же типа, что и класс, определяющий операцию (Point в этом примере):

// Более интеллектуальный тип Point.

public class Point

{

  ...

  // Перегруженная операция +.

  public static Point operator + (Point p1, Point p2)

    => new Point(p1.X + p2.X, p1.Y + p2.Y);

  // Перегруженная операция -.

  public static Point operator - (Point p1, Point p2)

    => new Point(p1.X - p2.X, p1.Y - p2.Y);

}

Логика, положенная в основу операции +, предусматривает просто возвращение нового объекта Point, основанного на сложении соответствующих полей входных параметров Point. Таким образом, когда вы пишете p1 + р2, "за кулисами" происходит следующий скрытый вызов статического метода operator +:

// Псевдокод: Point рЗ = Point.operator+ (pi, р2)

Point p3 = p1 + p2;

Аналогично выражение pi - р2 отображается так:

// Псевдокод: Point р4 = Point.operator- (pi, р2)

Point p4 = p1 - p2;

После произведенной модификации типа Point программа скомпилируется и появится возможность сложения и вычитания объектов Point, что подтверждает представленный далее вывод:

***** Fun with Overloaded Operators *****

ptOne = [100, 100]

ptTwo = [40, 40]

ptOne + ptTwo: [140, 140]

ptOne - ptTwo: [60, 60]

При перегрузке бинарной операции передавать ей два параметра того же самого типа не обязательно. Если это имеет смысл, тогда один из аргументов может относиться к другому типу. Например, ниже показана перегруженная операция +, которая позволяет вызывающему коду получить новый объект Point на основе числовой коррекции:

public class Point

{

  ...

  public static Point operator + (Point p1, int change)

    => new Point(p1.X + change, p1.Y + change);

  public static Point operator + (int change, Point p1)

    => new Point(p1.X + change, p1.Y + change);

}

Обратите внимание, что если вы хотите передавать аргументы в любом порядке, то потребуется реализовать обе версии метода (т.е. нельзя просто определить один из методов и рассчитывать, что компилятор автоматически будет поддерживать другой). Теперь новые версии операции + можно применять следующим образом:

// Выводит [110, 110].

Point biggerPoint = ptOne + 10;

Console.WriteLine("ptOne + 10 = {0}", biggerPoint);

//  Выводит [120, 120].

Console.WriteLine("10 + biggerPoint = {0}", 10 + biggerPoint);

Console.WriteLine();

А как насчет операций += и -=?

Если до перехода на C# вы имели дело с языком

1 ... 139 140 141 142 143 144 145 146 147 ... 407
На этой странице вы можете бесплатно читать книгу Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен бесплатно.

Оставить комментарий