Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
конспект.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
813.93 Кб
Скачать

9 Глава

Перегрузка операторов

Основы перегрузки операторов

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

Существуют две формы операторных методов (operator): одна — для унарных

операторов, другая — для бинарных.

// Общая форма перегрузки унарного оператора.

public static возвращаемый_тип operator op(тип_параметра операнд)

{

// операции

}

// Общая форма перегрузки бинарного оператора.

public static возвращаемый_тип operator op(тип_параметра1 операнд1, тип_параметра1 операнд2)

{

// операции

}

Здесь вместо ор подставляется перегружаемый оператор, например +или /; а возвращаемый_тип обозначает конкретный тип значения, возвращаемого указанной операцией. Это значение может быть любого типа, но зачастую оно указывается такого же типа, как и у класса, для которого перегружается оператор. Для унарных операторов операнд обозначает передаваемый операнд, а для бинарных операторов то же самое обозначают операнд1 и операнд2. Обратите внимание на то, что операторные методы должны иметь оба типа, public и static.

В параметрах оператора нельзя использовать модификатор ref или out.

Перегрузка бинарных операторов

//пример перегрузки бинарных операторов

using System;

//класс дляхранения трехмерных координат

class ThreeD

{

int x, y, z; //трехмерные координаты

public ThreeD() { x = y = z = 0; }

public ThreeD(int i, int j, int k) { x = i; y = j; z = k; }

//перегрузить бинарный оператор +

public static ThreeD operator +(ThreeD op1, ThreeD op2)

{

ThreeD result = new ThreeD();

// сложить коордитаны двух точек и возвратить результат.

result.x = op1.x + op2.x; //эти операторы выполняют

result.y = op1.y + op2.y; //целочисельное сложение,

result.z = op1.z + op2.z; //сохраняя свое исходное назначение.

return result;

}

//перегрузить бинарный оператор -.

public static ThreeD operator -(ThreeD op1, ThreeD op2)

{

ThreeD result = new ThreeD();

//обратить внимание на порядок следования операндов:

//op1 - левый операнд, а op2 - правый операнд.

result.x = op1.x - op2.x;//эти операторы

result.y = op1.y - op2.y;//выполняют целочисленное

result.z = op1.z - op2.z;//вычитание

return result;

}

//вывести координаты X, Y, Z.

public void Show()

{

Console.WriteLine(x + ", " + y + ", " + z);

}

}

class ThreeDDemo

{

static void Main()

{

ThreeD a = new ThreeD(1, 2, 3);

ThreeD b = new ThreeD(10, 10, 10);

ThreeD c;

Console.Write("Координаты точки а: ");

a.Show();

Console.WriteLine();

Console.Write("Координаты точки b: ");

b.Show();

Console.WriteLine();

c = a + b; //сложить координаты точек a and b

Console.Write("Результать сложения c = a + b: ");

c.Show();

Console.WriteLine();

c = a + b + c; // сложить координаты точек а, b и с

Console.Write("Результат сложения a + b + c: ");

c.Show();

Console.WriteLine();

c = c - a; //вычесть координаты точки a

Console.Write("Результать вычитания c - a: ");

c.Show();

Console.WriteLine();

c = c - b;//вычесть координаты точки b

Console.Write("Результать вычитания c - b: ");

c.Show();

Console.WriteLine();

}

}

Обратите внимание на то, что метод operator+() возвращает объект типа ThreeD. Этот метод мог бы возвратить значение любого допустимого в C# типа, но благодаря тому что он возвращает объект типа ThreeD, оператор + можно использовать в таких составных выражениях, как a+b+с.

Перегрузка унарных операторов

Унарные операторы перегружаются таким же образом, как и бинарные. Главное отличие заключается, конечно, в том, что у них имеется лишь один операнд. В качестве примера ниже приведен метод, перегружающий оператор унарного минуса для класса ThreeD.

// Перегрузить оператор унарного минуса.

public static ThreeD operator - (ThreeD op)

{

ThreeD result = new ThreeD ();

result.x = -op.x;

result.у = -op.у;

result.z = -op.z;

return result;

}

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

а = -b

является отрицательное значение операнда b, но сам операнд b не меняется.

В C# перегрузка операторов ++ и -- осуществляется довольно просто. Для этого достаточно возвратить инкрементированное или декрементированное значение, но не изменять вызывающий объект. А все остальное возьмет на себя компилятор С#, различая префиксные и постфиксные формы этих операторов.

// Перегрузить унарный оператор ++.

public static ThreeD operator ++(ThreeD op)

{

ThreeD result = new ThreeD();

// Возвратить результат инкрементирования.

result.x = op.x + 1;

result.у = op.у + 1;

result.z = op.z + 1;

return result;

}

___________________________________________________________________________________________________

//пример перегрузки бинарных и унарных операторов

using System;

//класс для хранения трехмерных координат

class ThreeD

{

int x, y, z; //трехмерные координаты

public ThreeD() { x = y = z = 0; }

public ThreeD(int i, int j, int k) { x = i; y = j; z = k; }

//перегрузить бинарный оператор +.

public static ThreeD operator +(ThreeD op1, ThreeD op2)

{

ThreeD result = new ThreeD();

//сложить координаты двух точек и возвратить результат.

result.x = op1.x + op2.x;

result.y = op1.y + op2.y;

result.z = op1.z + op2.z;

return result;

}

//перегрузить бинарный оператор -.

public static ThreeD operator -(ThreeD op1, ThreeD op2)

{

ThreeD result = new ThreeD();

//обратить внимание на порядок следования операндов:

//op1 - левый операнд, op2 - правый операнд.

result.x = op1.x - op2.x;

result.y = op1.y - op2.y;

result.z = op1.z - op2.z;

return result;

}

//перегрузить унарный оператор -.

public static ThreeD operator -(ThreeD op)

{

ThreeD result = new ThreeD();

result.x = -op.x;

result.y = -op.y;

result.z = -op.z;

return result;

}

//перегрузить унарный оператор ++.

public static ThreeD operator ++(ThreeD op)

{

ThreeD result = new ThreeD();

//возвратить результат инкреминирования.

result.x = op.x + 1;

result.y = op.y + 1;

result.z = op.z + 1;

return result;

}

//вывести координаты X, Y, Z.

public void Show()

{

Console.WriteLine(x + ", " + y + ", " + z);

}

}

class ThreeDDemo

{

static void Main()

{

ThreeD a = new ThreeD(1, 2, 3);

ThreeD b = new ThreeD(10, 10, 10);

ThreeD c = new ThreeD();

Console.Write("Координаты точки а: ");

a.Show();

Console.WriteLine();

Console.Write("Координаты точки b: ");

b.Show();

Console.WriteLine();

c = a + b;

Console.Write("Результат сложения a + b: ");

c.Show();

Console.WriteLine();

c = a + b + c;

Console.Write("Результат сложения a + b + c: ");

c.Show();

Console.WriteLine();

c = c - a;

Console.Write("Результат вычитания c - a: ");

c.Show();

Console.WriteLine();

c = c - b;

Console.Write("Результат вычитания c - b: ");

c.Show();

Console.WriteLine();

c = -a;

Console.Write("Результат присваивания -а: ");

c.Show();

Console.WriteLine();

c = a++;

Console.WriteLine("Если c = a++");

Console.Write("то координаты точки с равны ");

c.Show();

Console.Write("а координаты точки a равны ");

a.Show();

//установить исходные координаты (1,2,3) точки а

a = new ThreeD(1, 2, 3);

Console.Write("\nУстановка исходных координат точки а: ");

a.Show();

c = ++a;

Console.WriteLine("\nЕсли с = ++a");

Console.Write("то координаты точки с равны ");

c.Show();

Console.Write("а координаты точки а равны ");

a.Show();

}

}

Выполнение операций со встроенными в C# типами данных

// Перегрузить бинарный оператор + для сложения объекта

// типа ThreeD и целого значения типа int.

public static ThreeD operator +(ThreeD op1, int op2)

{

ThreeD result = new ThreeD();

result.x = op1.x + op2;

result.у = op1.y + op2;

result.z = op1.z + op2;

return result;

}

_____________________________________________________________________________________

// Перегрузить бинарный оператор + дважды:

// один раз - для сложения объектов класса ThreeD,

// а другой раз — для сложения объекта типа ThreeD и целого значения типа int.

using System;

// Класс для хранения трехмерных координат.

class ThreeD

{

int x, у, z; // трехмерные координаты

public ThreeD() { x = у = z = 0; }

public ThreeD(int i, int j, int k) { x = i; у = j; z = k; }

// Перегрузить бинарный оператор + для сложения объектов класса ThreeD.

public static ThreeD operator +(ThreeD op1, ThreeD op2)

{

ThreeD result = new ThreeD();

/* Сложить координаты двух точек и возвратить результат. */

result.x = op1.x + op2.x;

result.у = op1.у + op2.у;

result.z = op1.z + op2.z;

return result;

}

// Перегрузить бинарный оператор + для сложения

// объекта типа ThreeD и целого значения типа int.

public static ThreeD operator +(ThreeD op1, int op2)

{

ThreeD result = new ThreeD();

result.x = op1.x + op2;

result.у = op1.у + op2;

result.z = op1.z + op2;

return result;

}

// Вывести координаты X, Y, Z.

public void Show()

{

Console.WriteLine(x + ", " + у + ", " + z);

}

}

class ThreeDDemo

{

static void Main()

{

ThreeD a = new ThreeD(1, 2, 3);

ThreeD b = new ThreeD(10, 10, 10);

ThreeD с = new ThreeD();

Console.Write("Координаты точки a; ");

a.Show();

Console.WriteLine();

Console.Write("Координаты точки b: ");

b.Show();

Console.WriteLine();

с = a + b; // сложить объекты класса ThreeD

Console.Write("Результат сложения a + b: ");

с.Show();

Console.WriteLine();

с = b + 10; // сложить объект типа ThreeD и целое значение типа int

Console.Write("Результат сложения b + 10: ");

с.Show();

}

}

Продемонстрированная выше перегрузка оператора +, безусловно, расширяет полезные функции класса ThreeD, тем не менее, она делает это не до конца. И вот почему. Метод operator +(ThreeD, int) позволяет выполнять операции, подобные следующей.

ob1 = оb2 + 10;

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

ob1 = 10 + оb2;

// Перегрузить бинарный оператор + трижды:

// один раз — для сложения объектов класса ThreeD,

// второй раз — для сложения объекта типа ThreeD и целого значения типа int,

// а третий раз — для сложения целого значения типа int и объекта типа ThreeD.

using System;

// Класс для хранения трехмерных координат.

class ThreeD

{

int x, y, z; // трехмерные координаты

public ThreeD() { x = y = z = 0; }

public ThreeD(int i, int j, int k) { x = i; y = j; z = k; }

// Перегрузить бинарный оператор + для сложения объектов класса ThreeD.

public static ThreeD operator +(ThreeD op1, ThreeD op2)

{

ThreeD result = new ThreeD();

/* Сложить координаты двух точек и возвратить результат. */

result.x = op1.x + op2.x;

result.y = op1.y + op2.y;

result.z = op1.z + op2.z;

return result;

}

// Перегрузить бинарный оператор + для сложения

// объекта типа ThreeD и целого значения типа int.

public static ThreeD operator +(ThreeD op1, int op2)

{

ThreeD result = new ThreeD();

result.x = op1.x + op2;

result.y = op1.y + op2;

result.z = op1.z + op2;

return result;

}

// Перегрузить бинарный оператор + для сложения

// целого значения типа int и объекта типа ThreeD.

public static ThreeD operator +(int op1, ThreeD op2)

{

ThreeD result = new ThreeD();

result.x = op2.x + op1;

result.y = op2.y + op1;

result.z = op2.z + op1;

return result;

}

// Вывести координаты X, Y, Z.

public void Show()

{

Console.WriteLine(x + ", " + y + ", " + z);

}

}

class ThreeDDemo

{

static void Main()

{

ThreeD a = new ThreeD(1, 2, 3);

ThreeD b = new ThreeD(10, 10, 10);

ThreeD c = new ThreeD();

Console.Write("Координаты точки a: ");

a.Show();

Console.WriteLine();

Console.Write("Координаты точки b: ");

b.Show();

Console.WriteLine();

c = a + b; // сложить объекты класса ThreeD

Console.Write("Результат сложения a + b: ");

c.Show();

Console.WriteLine();

c = b + 10; // сложить объект типа ThreeD и целое значение типа int

Console.Write("Результат сложения b + 10: ");

c.Show();

Console.WriteLine();

c = 15 + b; // сложить целое значение типа int и объект типа ThreeD

Console.Write("Результат сложения 15 + b: ");

c.Show();

}

}

Перегрузка операторов отношения

Операторы отношения, например == и <, могут также перегружаться, причем очень просто. Как правило, перегруженный оператор отношения возвращает логическое значение true и false.

//перегрузить операторы < and >.

using System;

//класс для храниненя трехмерных координат.

class ThreeD

{

int x, y, z;

public ThreeD() { x = y = z = 0; }

public ThreeD(int i, int j, int k) { x = i; y = j; z = k; }

//прегрузить оператор <.

public static bool operator <(ThreeD op1, ThreeD op2)

{

if (Math.Sqrt(op1.x * op1.x + op1.y * op1.y + op1.z * op1.z) <

Math.Sqrt(op2.x * op2.x + op2.y * op2.y + op2.z * op2.z))

return true;

else return false;

}

//перегрузить оператор >.

public static bool operator >(ThreeD op1, ThreeD op2)

{

if (Math.Sqrt(op1.x * op1.x + op1.y * op1.y + op1.z * op1.z) >

Math.Sqrt(op2.x * op2.x + op2.y * op2.y + op2.z * op2.z))

return true;

else return false;

}

//вывести координаты X, Y, Z.

public void Show()

{

Console.WriteLine(x + ", " + y + ", " + z);

}

}

class ThreeDDemo

{

static void Main()

{

ThreeD a = new ThreeD(5, 6, 7);

ThreeD b = new ThreeD(10, 10, 10);

ThreeD c = new ThreeD(1, 2, 3);

ThreeD d = new ThreeD(6, 7, 5);

Console.Write("Координаты точки a: ");

a.Show();

Console.Write("Координаты точки b: ");

b.Show();

Console.Write("Координаты точки c: ");

c.Show();

Console.Write("Координаты точки d: ");

d.Show();

Console.WriteLine();

if (a > c) Console.WriteLine("a > c true");

if (a < c) Console.WriteLine("a < c thre");

if (a > b) Console.WriteLine("a > b true");

if (a < b) Console.WriteLine("a < b true");

if (a > d) Console.WriteLine("a > d true");

else if (a < d) Console.WriteLine("a < d true");

else Console.WriteLine("Точки a и d находятся на одном расстоянии от начала отсчета");

}

}

На перегрузку операторов отношения накладывается следующее важное ограничение: они должны перегружаться попарно. Так, если перегружается оператор <, то следует перегрузить и оператор >, и наоборот. Ниже приведены составленные в пары перегружаемые операторы отношения.

== !=

< >

<= >=

И еще одно замечание: если перегружаются операторы == и !=, то для этого обычно требуется также переопределить методы Object.Equals() и Object.GetHashCode(). Эти методы и способы их переопределения подробнее рассматриваются в главе 11.

Перегрузка операторов true и false

Ключевые слова true и false можно также использовать в качестве унарных операторов для целей перегрузки. Операторы true и false должны перегружаться попарно, а не раздельно.

public static bool operator true(тип_параметра операнд)

{

// Возврат логического значения true или false.

}

public static bool operator false(тип_параметра операнд)

{

// Возврат логического значения true или false.

}

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

//перегрузить операторы < and >.

using System;

//класс для храниненя трехмерных координат.

class ThreeD

{

int x, y, z;

public ThreeD() { x = y = z = 0; }

public ThreeD(int i, int j, int k) { x = i; y = j; z = k; }

// Перегрузить оператор true.

public static bool operator true(ThreeD op)

{

if ((op.x != 0) || (op.y != 0) || (op.z != 0))

return true; // хотя бы одна координата не равна нулю

else

return false;

}

// Перегрузить оператор false.

public static bool operator false(ThreeD op)

{

if ((op.x == 0) && (op.y == 0) && (op.z == 0))

return true; // все координаты равны нулю

else

return false;

}

// Перегрузить унарный оператор --.

public static ThreeD operator --(ThreeD op)

{

ThreeD result = new ThreeD();

// Возвратить результат декрементирования.

result.x = op.x - 1;

result.y = op.y - 1;

result.z = op.z - 1;

return result;

}

// Вывести координаты X, Y, Z.

public void Show()

{

Console.WriteLine(x + ", " + y + ", " + z);

}

}

class TrueFalseDemo

{

static void Main()

{

ThreeD a = new ThreeD(5, 6, 7);

ThreeD b = new ThreeD(10, 10, 10);

ThreeD c = new ThreeD(0, 0, 0);

Console.Write("Координаты точки a: ");

a.Show();

Console.Write("Координаты точки b: ");

b.Show();

Console.Write("Координаты точки с: ");

c.Show();

Console.WriteLine();

if (a) Console.WriteLine("Точка а истинна.");

else Console.WriteLine("Точка а ложна.");

if (b) Console.WriteLine("Точка b истинна.");

else Console.WriteLine("Точка b ложна.");

if (c) Console.WriteLine("Точка с истинна.");

else Console.WriteLine("Точка с ложна.");

Console.WriteLine();

Console.WriteLine("Управление циклом с помощью объекта класса ThreeD.");

do

{

b.Show();

b--;

}

while (b);

}

}

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

Как вам должно быть уже известно, в C# предусмотрены следующие логические операторы: &, |, !, && и ||. Из них перегрузке, безусловно, подлежат только операторы &, | и !. Тем не менее, соблюдая определенные правила, можно извлечь также пользу из укороченных логических операторов && и ||. Все эти возможности рассматриваются ниже.

Простой способ перегрузки логических операторов

// Простой способ перегрузки логических операторов

// !, | и & для объектов класса ThreeD.

using System;

// Класс для хранения трехмерных координат.

class ThreeD

{

int x, y, z; // трехмерные координаты

public ThreeD() { x = y = z = 0; }

public ThreeD(int i, int j, int k) { x = i; y = j; z = k; }

// Перегрузить логический оператор |.

public static bool operator |(ThreeD op1, ThreeD op2)

{

if (((op1.x != 0) || (op1.y != 0) || (op1.z != 0)) |

((op2.x != 0) || (op2.y != 0) || (op2.z != 0)))

return true;

else

return false;

}

// Перегрузить логический оператор &.

public static bool operator &(ThreeD op1, ThreeD op2)

{

if (((op1.x != 0) && (op1.y != 0) && (op1.z != 0)) &

((op2.x != 0) && (op2.y != 0) && (op2.z != 0)))

return true;

else

return false;

}

// Перегрузить логический оператор !.

public static bool operator !(ThreeD op)

{

if ((op.x != 0) || (op.y != 0) || (op.z != 0))

return false;

else return true;

}

// Вывести координаты X, Y, Z.

public void Show()

{

Console.WriteLine(x + ", " + y + ", " + z);

}

}

class TrueFalseDemo

{

static void Main()

{

ThreeD a = new ThreeD(5, 6, 7);

ThreeD b = new ThreeD(10, 10, 10);

ThreeD c = new ThreeD(0, 0, 0);

Console.Write("Координаты точки a: ");

a.Show();

Console.Write("Координаты точки b: ");

b.Show();

Console.Write("Координаты точки с: ");

c.Show();

Console.WriteLine();

if (!a) Console.WriteLine("Точка а ложна.");

if (!b) Console.WriteLine("Точка b ложна.");

if (!c) Console.WriteLine("Точка с ложна.");

Console.WriteLine();

if (a & b) Console.WriteLine("a & b истинно.");

else Console.WriteLine("a & b ложно.");

if (a & c) Console.WriteLine("a & с истинно.");

else Console.WriteLine("a & с ложно.");

if (a | b) Console.WriteLine("a | b истинно.");

else Console.WriteLine("a | b ложно.");

if (a | c) Console.WriteLine("a | с истинно.");

else Console.WriteLine("a | с ложно.");

}

}

При таком способе перегрузки логических операторов &, | и ! методы каждого из них возвращают результат типа bool. Это необходимо для того, чтобы использовать рассматриваемые операторы обычным образом, т.е. в тех выражениях, где предполагается результат типа bool. Напомним, что для всех встроенных в C# типов данных результатом логической операции должно быть значение типа bool. Поэтому вполне разумно предусмотреть возврат значения типа bool и в перегружаемых вариантах этих логических операторов. Но, к сожалению, такой способ перегрузки пригоден лишь в том случае, если не требуются укороченные логические операторы.

Как сделать укороченные логические операторы доступными для применения

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

  1. Во-первых, в классе должна быть произведена перегрузка логических операторов & и |.

  2. Во-вторых, перегружаемые методы операторов & и | должны возвращать значение того же типа, что и у класса, для которого эти операторы перегружаются.

  3. В-третьих, каждый параметр должен содержать ссылку на объект того класса, для которого перегружается логический оператор.

  4. И в-четвертых, для класса должны быть перегружены операторы true и false.

Если все эти условия выполняются, то укороченные логические операторы автоматически становятся пригодными для применения.

/*Более совершенный способ перегрузки логических

операторов !, | и & для обьектов класса ThreeD.

В этом варианте укороченные логические операторы && и ||

становятся доступными для применения автоматически. */

using System;

//Класс для хранения трехмерных координат.

class ThreeD

{

int x, y, z;

public ThreeD() { x = y = z = 0; }

public ThreeD(int i, int j, int k) { x = i; y = j; z = k; }

// Перегрузить логический оператор | для укороченного вычисления.

public static ThreeD operator |(ThreeD op1, ThreeD op2)

{

if (((op1.x != 0) || (op1.y != 0) || (op1.z != 0)) |

((op2.x != 0) || (op2.y != 0) || (op2.z != 0)))

return new ThreeD(1, 1, 1);

else

return new ThreeD(0, 0, 0);

}

// Перегрузить логический оператор & для укороченного вычисления.

public static ThreeD operator &(ThreeD op1, ThreeD op2)

{

if (((op1.x != 0) && (op1.y != 0) && (op1.z != 0)) &

((op2.x != 0) && (op2.y != 0) && (op2.z != 0)))

return new ThreeD(1, 1, 1);

else

return new ThreeD(0, 0, 0);

}

// Перегрузить логический оператор !.

public static bool operator !(ThreeD op)

{

if (op) return false;

else return true;

}

// Перегрузить оператор true.

public static bool operator true(ThreeD op)

{

if ((op.x != 0) || (op.y != 0) || (op.z != 0))

return true; // хотя бы одна координата не равна нулю

else

return false;

}

// Перегрузить оператор false.

public static bool operator false(ThreeD op)

{

if ((op.x == 0) && (op.y == 0) && (op.z == 0))

return true; // все координаты равны нулю

else

return false;

}

// Ввести координаты X, Y, Z.

public void Show()

{

Console.WriteLine(x + ", " + y + ", " + z);

}

}

class TrueFalseDemo

{

static void Main()

{

ThreeD a = new ThreeD(5, 6, 7);

ThreeD b = new ThreeD(10, 10, 10);

ThreeD c = new ThreeD(0, 0, 0);

Console.Write("Координаты точки a: ");

a.Show();

Console.Write("Координаты точки b: ");

b.Show();

Console.Write("Координаты точки с: ");

c.Show();

Console.WriteLine();

if (a) Console.WriteLine("Точка а истинна.");

if (b) Console.WriteLine("Точка b истинна.");

if (c) Console.WriteLine("Точка с истинна.");

if (!a) Console.WriteLine("Точка а ложна.");

if (!b) Console.WriteLine("Точка b ложна.");

if (!c) Console.WriteLine("Точка с ложна.");

Console.WriteLine();

Console.WriteLine("Применение логических операторов & и |");

if (a & b) Console.WriteLine("а & b истинно.");

else Console.WriteLine("а & b ложно.");

if (a & c) Console.WriteLine("а & с истинно.");

else Console.WriteLine("а & с ложно.");

if (a | b) Console.WriteLine("a | b истинно.");

else Console.WriteLine("а | b ложно.");

if (a | c) Console.WriteLine("а | с истинно.");

else Console.WriteLine("а | с ложно.");

Console.WriteLine();

// А теперь применить укороченные логические операторы.

Console.WriteLine("Применение укороченных логических операторов && и ||");

if (a && b) Console.WriteLine("a && b истинно.");

else Console.WriteLine("а && b ложно.");

if (a && c) Console.WriteLine("а && с истинно.");

else Console.WriteLine("a && с ложно.");

if (a || b) Console.WriteLine("a || b истинно.");

else Console.WriteLine("a || b ложно.");

if (a || c) Console.WriteLine("a || с истинно.");

else Console.WriteLine("a || с ложно.");

}

}

Операторы преобразования

Иногда объект определенного класса требуется использовать в выражении, включающем в себя данные других типов. В одних случаях для этой цели оказывается пригодной перегрузка одного иди более операторов, а в других случаях — обыкновенное преобразование типа класса в целевой тип. Для подобных ситуаций в C# предусмотрена специальная разновидность операторного метода, называемая оператором преобразования. Такой оператор преобразует объект исходного класса в другой тип.

Существуют две формы операторов преобразования: явная и неявная.

public static explicit operator целевой_тип(исходный_тип v) { return значение; }

public static implicit operator целевой_тип(исходный_тип v) { return значение; }

где целевой_тип обозначает тот тип, в который выполняется преобразование; исходный_тип — тот тип, который преобразуется; значение — конкретное значение, приобретаемое классом после преобразования. Операторы преобразования возвращают данные, имеющие целевой_тип, причем указывать другие возвращаемые типы данных не разрешается.

Если оператор преобразования указан в неявной форме (implicit), то преобразование вызывается автоматически, например, в том случае, когда объект используется в выражении вместе со значением целевого типа. Если же оператор преобразования указан в явной форме (explicit), то преобразование вызывается в том случае, когда выполняется приведение типов.

Создадим оператор преобразования специально для класса ThreeD, чтобы продемонстрировать его применение. Допустим, что требуется преобразовать объект типа ThreeD в целое значение, чтобы затем использовать его в целочисленном выражении. Такое преобразование требуется, в частности, для получения произведения всех трех координат объекта.

public static implicit operator int(ThreeD op1)

{

return op1.x * op1.y * op1.z;

}

_____________________________________________________________________________________

//пример применения оператора неявного преобразования

using System;

class ThreeD

{

int x, y, z;

public ThreeD() { x = y = z = 0; }

public ThreeD(int i, int j, int k) { x = i; y = j; z = k; }

//перегрузить бинарный оператор +

public static ThreeD operator +(ThreeD op1, ThreeD op2)

{

ThreeD result = new ThreeD();

result.x = op1.x + op2.x;

result.y = op1.y + op2.y;

result.z = op1.z + op2.z;

return result;

}

//неявное преобразование обьекта типа Threed к типу int

public static implicit operator int(ThreeD op1)

{

return op1.x * op1.y * op1.z;

}

//вывести координаты X, Y, Z.

public void Show()

{

Console.WriteLine(x + ", " + y + ", " + z);

}

}

class ThreeDDemo

{

static void Main()

{

ThreeD a = new ThreeD(1, 2, 3);

ThreeD b = new ThreeD(10, 10, 10);

ThreeD c = new ThreeD();

int i;

Console.Write("Координаты точки a: ");

a.Show();

Console.WriteLine();

Console.Write("Координаты точки b: ");

b.Show();

Console.WriteLine();

c = a + b;

Console.Write("Результат сложения a + b: ");

c.Show();

Console.WriteLine();

i = a; //преобразовать в тип int

Console.WriteLine("Результат присваивания i = a: " + i);

Console.WriteLine();

i = a * 2 - b; //преобразовать в тип int

Console.WriteLine("Результат вычисления выражения a * 2 - b: " + i);

}

}

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

//пример применения оператора неявного преобразования

using System;

class ThreeD

{

int x, y, z;

public ThreeD() { x = y = z = 0; }

public ThreeD(int i, int j, int k) { x = i; y = j; z = k; }

//перегрузить бинарный оператор +

public static ThreeD operator +(ThreeD op1, ThreeD op2)

{

ThreeD result = new ThreeD();

result.x = op1.x + op2.x;

result.y = op1.y + op2.y;

result.z = op1.z + op2.z;

return result;

}

//выполнить явное преобразование обьекта типа Threed к типу int

public static explicit operator int(ThreeD op1)

{

return op1.x * op1.y * op1.z;

}

//вывести координаты X, Y, Z.

public void Show()

{

Console.WriteLine(x + ", " + y + ", " + z);

}

}

class ThreeDDemo

{

static void Main()

{

ThreeD a = new ThreeD(1, 2, 3);

ThreeD b = new ThreeD(10, 10, 10);

ThreeD c = new ThreeD();

int i;

Console.Write("Координаты точки a: ");

a.Show();

Console.WriteLine();

Console.Write("Координаты точки b: ");

b.Show();

Console.WriteLine();

c = a + b;

Console.Write("Результат сложения a + b: ");

c.Show();

Console.WriteLine();

i = (int)a; //преобразовать в тип int

Console.WriteLine("Результат присваивания i = a: " + i);

Console.WriteLine();

i = (int)a * 2 - (int)b; //преобразовать в тип int

Console.WriteLine("Результат вычисления выражения a * 2 - b: " + i);

}

}

На операторы преобразования накладывается ряд следующих ограничений.

  • Исходный или целевой тип преобразования должен относиться к классу, для которого объявлено данное преобразование. В частности, нельзя переопределить преобразование в тип int, если оно первоначально указано как преобразование в тип double.

  • Нельзя указывать преобразование в класс object или же из этого класса.

  • Для одних и тех же исходных и целевых типов данных нельзя указывать одновременно явное и неявное преобразование.

  • Нельзя указывать преобразование базового класса в производный класс. (Подробнее о базовых и производных классах речь пойдет в главе 11.)

  • Нельзя указывать преобразование в интерфейс или же из него. (Подробнее обинтерфейсах — в главе 12.)

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

Рекомендации и ограничения по перегрузке операторов

На перегрузку операторов накладывается ряд ограничений. В частности, нельзя изменять приоритет любого оператора или количество операндов, которое требуется для оператора, хотя в операторном методе можно и проигнорировать операнд. Кроме того, имеется ряд операторов, которые нельзя перегружать. А самое главное, что перегрузке не подлежит ни один из операторов присваивания, в том числе и составные, как, например, оператор +=. Ниже перечислены операторы, которые нельзя перегружать. Среди них имеются и такие операторы, которые будут рассматриваться далее в этой книге.

&& () . ?

?? [] || =

=> -> as checked

Default is new sizeof

typeof unchecked

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

Ограничение, связанное с тем, что некоторые операторы, например +=, нельзя перегружать, на самом деле не является таким уж непреодолимым. Вообще говоря, если оператор определен как перегружаемый и используется в составном операторе присваивания, то обычно вызывается метод этого перегружаемого оператора. Следовательно, при обращении к оператору += в программе автоматически вызывается заранее объявленный вариант метода operator+(). Например, в приведенном ниже фрагменте кода метод operator+() автоматически вызывается для класса ThreeD, а в итоге объект b будет содержать координаты 11, 12, 13.

ThreeD а = new ThreeD(l, 2, 3);

ThreeD b = new ThreeD(10, 10, 10);

b += a; // сложить координаты точек а и b

Результат сложения b += a 11, 12, 13.

Еще один пример перегрузки операторов

В данном примере разрабатывается 4-разрядный целочисленный тип данных и для него определяется ряд операций. Вам, вероятно, известно, что на ранней стадии развития вычислительной техники широко применялся тип данных для обозначения 4-разрядных двоичных величин, называвшихся полубайтами, поскольку они составляли половину байта, содержали одну шестнадцатеричную цифру и были удобны для ввода кода полубайтами с пульта ЭВМ, что в те времена считалось привычным занятием для программистов! В наше время этот тип данных применяется редко, но он по-прежнему является любопытным дополнением целочисленных типов данных в С#. По традиции полубайт обозначает целое значение без знака.

В приведенном ниже примере программы тип полубайтовых данных реализуется с помощью класса Nybble. В качестве базового для него используется тип int, но с ограничением на хранение данных от 0 до 15. В классе Nybble определяются следующие операторы.

  • Сложение двух объектов типа Nybble.

  • Сложение значения типа intс объектом типа Nybble.

  • Сложение объекта типа Nybble со значением типа int.

  • Операции сравнения: больше (>) и меньше (<).

  • Операция инкремента.

  • Преобразование значения типа int в объект типа Nybble.

  • Преобразование объекта типа Nybble в значение типа int.

//создать полубайтовый тип 4-разрядных данных под названием Nybble.

using System;

//тип 4-разрядных данных.

class Nybble

{

int val; //базовый тип для хранения данных

public Nybble() { val = 0; }

public Nybble(int i)

{

val = i;

val = val & 0xF;//сохранить 4 младших разряда

}

//перегрузить бинарный оператор + для сложения двух обьектов типа Nybble

public static Nybble operator +(Nybble op1, Nybble op2)

{

Nybble result = new Nybble();

result.val = op1.val + op2.val;

result.val = result.val & 0xF; // сохранить 4 младших разряда

return result;

}

//перегрузить бинарный оператор + для сложения значения типа Nybble. и обьекта типа int

public static Nybble operator +(Nybble op1, int op2)

{

Nybble result = new Nybble();

result.val = op1.val + op2;

result.val = result.val & 0xF;//сохранить 4 младших разряда

return result;

}

//перегрузить бинарный оператор + для сложения значения типа int и обьекта типа Nybble.

public static Nybble operator +(int op1, Nybble op2)

{

Nybble result = new Nybble();

result.val = op1 + op2.val;

result.val = result.val & 0xF; //сохранить 4 младших разряда

return result;

}

//перегрузить оператор ++.

public static Nybble operator ++(Nybble op)

{

Nybble result = new Nybble();

result.val = op.val + 1;

result.val = result.val & 0xF; //сохранить 4 младших разряда

return result;

}

//перегрузить оператор >.

public static bool operator >(Nybble op1, Nybble op2)

{

if (op1.val > op2.val) return true;

else return false;

}

//перегрузить оператор <.

public static bool operator <(Nybble op1, Nybble op2)

{

if (op1.val < op2.val) return true;

else return false;

}

//преобразовать тип Nybble в тип int

public static implicit operator int(Nybble op)

{

return op.val;

}

//преобразовать тип int в тип Nybble.

public static implicit operator Nybble(int op)

{

return new Nybble(op);

}

}

class NybblseDemo

{

static void Main()

{

Nybble a = new Nybble(1);

Nybble b = new Nybble(10);

Nybble c = new Nybble();

int t;

Console.WriteLine("a: " + (int)a);

Console.WriteLine("b: " + (int)b);

//использовать тип Nybble в условном операторе if.

if (a < b) Console.WriteLine("a меньше b\n");

//сложить два обьекта типа Nybblt/

c = a + b;

Console.WriteLine("с после операции с = a + b: " + (int)c);

//сложить значение типа int c обьектом типа Nybble.

a += 5;

Console.WriteLine("а после операции a += 5: " + (int)a);

Console.WriteLine();

//использовать тип Nybble в выражении типа int.

t = a * 2 + 3;

Console.WriteLine("Результат вычисления выражения a * 2 + 3: " + t);

Console.WriteLine();

//продемонстрировать присваивание значения типа int и переполнение.

a = 19;

Console.WriteLine("Результат присваивания a = 19: " + (int)a);

Console.WriteLine();

//использовать тип Nybble для управления циклом.

Console.WriteLine("Управление циклом for c помощью обьекта типа Nybble.");

for (a = 0; a < 10; a++)

Console.Write((int)a + " ");

Console.WriteLine();

}

}