Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Методичка - Основи Програмування C_.doc
Скачиваний:
46
Добавлен:
18.12.2018
Размер:
1.44 Mб
Скачать

Перевантаження операцій в мові с#.

1. Загальні відомості.

При реалізації класів часто зручно передбачати можливість виконання певних операції над екземплярами класів. Наприклад, ви створюєте клас, що реалізує простір геометричних векторів. Необхідно мати можливість виконувати над об’єктами прийняті в математиці операції, як то: додавання та віднімання векторів, множення їх на число та між собою, тощо. Звісно для реалізації подібних операції можна створити методи-члени класу, проте значно зручніше мати змогу застосування цих операцій безпосередньо до об’єктів класу. Для цього в мові передбачене перевантаження операцій.

Для перевантаження операцій в класі створюється операторний метод – член класу, позначений службовим словом operator. Як відомо, операції бувають унарні та бінарні. Для перевантаження унарної операції використовується наступний синтаксис:

public static <ідентифікатор_класу> operator op (<ідентифікатор_класу> <операнд>)

{

// код операції

}

Для перевантаження бінарної операції синтаксис операторної функції буде наступний:

public static <тип_результату_операції> operator op (<тип_операнду_1> <операнд_1>, <тип_операнду_2> <операнд_2>)

{

// код операції

}

Тут символами op позначений знак операції, що перевантажується. Для бінарної операції тип принаймні одного операнду має бути типом класу, для якого перевантажується операція. Таким чином, операції можливо перевантажувати лише для класів, створюваних користувачем, а не для існуючих класів. Крім того, неможливо змінити пріоритет операцій, а також використати якісь нові знаки крім існуючих для операцій. Існує також ряд операцій, заборонених для перевантаження, але до них ми повернемось пізніше.

2. Перевантаження бінарних арифметичних операцій.

Створимо клас d2Vector геометричних векторів на площині, для якого пізніше і перевантажимо ряд операцій. Визначення класу помістимо в окремий файл проекту, наприклад, d2Vector.cs.

using System;

namespace ReloadOperators {

class d2Vector

{

double x, y;

public d2Vector(double x_, double y_) // конструктор

{ x = x_; y = y_; }

// конструктор без параметрів

public d2Vector() : this(1, 1) { }

// конструктор

public d2Vector(d2Vector v) : this(v.x, v.y) { }

// заміщений метод із System.Object

public override string ToString()

{ return String.Format("x = {0} y = {1}", x, y); }

}

}

Тут заміщається метод ToString() із класу System.Object – це дозволить пізніше зручно виводити координати векторів.

У файл Program.cs помістимо метод Main(). У ньому створимо вектори a та b (які саме конструктори спрацьовують при цьому?):

using System;

namespace ReloadOperators

{

class Program

{

static void Main()

{

d2Vector a = new d2Vector(1, 2);

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

d2Vector b = new d2Vector(3, 5);

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

}

}

}

Завдяки заміщеному методу ToString() координати тепер легко виводити на екран:

a: x = 1 y = 2

b: x = 3 y = 5

Наша мета – мати змогу написати, наприклад, a = a + b або a = a * b. Це і буде означати перевантаження операцій для класу.

Додамо у визначення класу d2Vector операторні методи для операцій додавання та віднімання двох векторів:

// бінарний плюс

public static d2Vector operator +(d2Vector v1, d2Vector v2)

{

d2Vector temp = new d2Vector(v1.x + v2.x, v1.y + v2.y);

return temp;

}

// бінарний мінус

public static d2Vector operator -(d2Vector v1, d2Vector v2)

{

d2Vector temp = new d2Vector(v1.x - v2.x, v1.y - v2.y);

return temp;

}

Код цих методів цілком зрозумілий – необхідно створити новий вектор, координати якого є відповідно сумою або різницею координат двох векторів-параметрів. Це зовсім просто завдяки тому, що у нас у класі є відповідний конструктор. Далі одержаний вектор повертається як результат кожного з цих методів. Помістивши тепер у Main() інструкції

d2Vector c = new d2Vector();

c = a + b;

Console.WriteLine("c = a + b: " + c.ToString());

c = a - b;

Console.WriteLine("c = a - b: " + c.ToString());

одержимо на екрані наступні результати:

c = a + b: x = 4 y = 7

c = a - b: x = -2 y = -3

Далі реалізуємо у класі d2Vector можливість множення векторів. Тут слід мати на увазі, що вектори можна множити між собою скалярно, а також множити вектор на число і навпаки – число на вектор (для операторного методу порядок операндів суттєвий!). Таким чином нам необхідно три перевантажених методи множення векторів – при скалярному множенні результатом буде дійсне число, при множенні вектора та числа між собою – результатом буде вектор.

// скалярний добуток

public static double operator *(d2Vector v1, d2Vector v2)

{ return v1.x * v2.x + v1.y * v2.y; }

// множення вектора на число

public static d2Vector operator *(d2Vector v1, double k)

{

d2Vector temp = new d2Vector(v1.x * k, v1.y * k);

return temp;

}

// множення числа на вектор

public static d2Vector operator *(double k, d2Vector v1)

{

d2Vector temp = new d2Vector(v1.x * k, v1.y * k);

return temp;

}

Операторний метод множення числа на вектор після того, як визначене множення вектора на число можна було створити і в інший спосіб:

// множення числа на вектор

public static d2Vector operator *(double k, d2Vector v1)

{

return v1 * k; // тут викликаємо попередній метод

}

Тут просто викликається попередній операторний метод, який множить вектор на число.

Зверніть вагу, що при множенні числа та вектору для результату також створюється новий вектор, адже координати вектора-множника не повинні змінюватись, принаймні так це відбувається із справжніми векторами.

Тепер можемо протестувати новий фрагмент коду – включимо у метод Main() наступні інструкції:

double res = a * b;

Console.WriteLine("a * b = {0} ", res);

c = 2 * b;

Console.WriteLine("c = 2 * b: " + c.ToString());

c = a * 5;

Console.WriteLine("c = a * 5: " + c.ToString());

В результаті одержимо:

a * b = 13

c = 2 * b: x = 6 y = 10

c = a * 5: x = 5 y = 10

Таким чином ми створили можливість використання вбудованих операцій визначених для числових операндів також і для екземплярів нашого класу. При цьому зміст та результат операцій ми визначаємо, виходячи із змісту класу.