- •Тема 6. Основы программирования на языке c#
- •Условная операция
- •Операции checked и unchecked
- •Операция поглощения null
- •Безопасность типов
- •Преобразования типов
- •Неявные преобразования
- •Явные преобразования
- •Проверка равенства объектов
- •Упаковка и распаковка
- •Виртуальный метод Equals()
- •Статический метод Equals()
- •Проверка типов значений на равенство
- •Перегрузка операций
- •Как работают операции
- •Пример перегрузки операции: структура Vector
- •Добавление дополнительных перегрузок
- •Перегрузка операций сравнения
- •Пользовательские приведения
- •Реализация пользовательских приведений
- •Приведение между классами
- •Приведение между базовым и производным классами
- •Упаковывающие и распаковывающие приведения
- •Множественные приведения
Пример перегрузки операции: структура Vector
В этом разделе демонстрируется перегрузка операции на примере структуры Vector, представляющей трехмерный математический вектор. Не волнуйтесь, если не очень-то разбираетесь в математике мы постараемся предельно упростить данный пример. Прежде всего, поясним, что трехмерный вектор это просто три числа (типа double), которые сообщают, насколько далеко перемещается нечто. Переменные, хранящие эти три числа, называются х, у и z: х сообщает, насколько далеко нечто перемещается на восток, у на север, a z вверх. Комбинация этих трех чисел дает итоговое перемещение. Например, если х=3.0, у=3.0 и z=l.0 (что обычно записывается как (3.0,3.0,1.0)), это значит, что вы переместились на 3 единицы на восток, 3 на север и поднялись на 1 единицу вверх.
Вы можете складывать или перемножать векторы с другими векторами или числами. Кстати, в этом контексте мы будем использовать термин скаляр, который на языке математики означает просто число в терминологии C# это обычный double. Смысл сложения должен быть ясен. Если вы сначала переместитесь по вектору (3.0,3.0,1.0),а затем по вектору (2.0,-4.0,-4.0), то результирующее перемещение может быть вычислено простым сложением двух векторов. Сложение векторов означает индивидуальное сложение каждого из их компонентов, поэтому в результате получаем (5.0,-1.0,-3.0). В данном контексте математики пишут c=a+b, где а и b суммируемые векторы, а с результирующий вектор. Хорошо бы меть возможность так же обращаться со структурами Vector.
Тот факт, что в этом примере мы имеем дело со структурами, а не с классами, не важен. Перегрузка операций работает одинаково и с классами, и со структурами.
Ниже приведено определение Vector, содержащее поля-члены, конструкторы, переопределение ToString(), которое позволяет легко просмотреть содержимое Vector и перегруженную операцию.
namespace Wrox.ProCSharp.OOCSharp
{
struct Vector
{
public double x, y, z;
public Vector(double x, double y, double z)
{
this.x = x;
this.у = у;
this.z = z;
}
public Vector(Vector rhs)
{
x = rhs.x;
у = rhs.y;
z = rhs.z;
}
public override string ToString()
{
return " ( " + x + " , " + у + " , " + z + " ) ";
}
// ...
}
// ...
}
В этом примере представлено два конструктора, которые требуют указания начального значения вектора либо в виде значений всех компонентов, либо по значению другого Vector, значения компонентов которого можно скопировать.
Конструкторы вроде второго, принимающие единственный аргумент типа Vector, часто называют конструкторами копирования, поскольку они позволяют инициализировать экземпляр класса или структуры простым копированием другого экземпляра. Обратите внимание, что для простоты мы оставляем поля структуры общедоступными (public). Их можно было бы сделать приватными (private) и написать соответствующие свойства для доступа, но это никак не повлияло бы на данный пример, за исключением того, что удлинило бы код.
Самая интересная часть структуры Vector перегрузка операции сложения:
public static Vector operator+ (Vector lhs, Vector rhs)
{
Vector result = new Vector(lhs);
result.x += rhs.x;
result.у += rhs.y;
result.z += rhs.z;
return result;
}
Перегрузка операции объявляется почти так же, как метод, за исключением того, что ключевое слово operator сообщает компилятору, что это на самом деле определение перегрузки операции. За ключевым словом operator следует символ операции, в данном случае знак сложения (+). Возвращаемый тип это тип, который получается после применения этой операции. Сложение двух векторов дает в результате вектор, поэтому здесь возвращаемым типом является Vector. В данном конкретном случае перегрузки операции сложения возвращаемый тип совпадает с включающим его классом, но, как будет показано позже, это вовсе не обязательно. Два параметра это операнды, с которыми операция будет работать. Для бинарных операций (которые принимают два параметра), подобных сложению и вычитанию, первый параметр это значение, которое записывается слева от знака операции, а второй значение, записываемое справа.
Следует обратить внимание, что левые параметры принято называть lhs (left-hand side), а правые rhs (right-handside).
C# требует чтобы перегруженные операции объявлялись как public и static; это значит, что они ассоциированы с классом или структурой, а не с их экземплярами. По этой причине тело перегруженной операции не имеет доступа к нестатическим членам, а также к идентификатору this. И это нормально, потому что параметры предоставляют все входные данные, необходимые операции для решения своей задачи.
Теперь, если вы поняли синтаксис объявления операции сложения, то можете взглянуть на то, что происходит внутри нее:
{
Vector result = new Vector (lhs) ;
result.x += rhs.x;
result.у += rhs.y;
result.z += rhs.z;
return result;
}
Эта часть кода точно такая же, какая могла бы быть при определении метода, и несложно догадаться, что она возвращает вектор, представляющий сумму lhs и rhs. Вы просто индивидуально складываете члены х, у и z. Теперь потребуется написать некоторый простой код, чтобы протестировать структуру Vector:
static void Main()
{
Vector vect1, vect2, vect3;
vect1 = new Vector(3.0,3.0,1.0);
vect2 = new Vector(2.0,-4.0,-4.0);
vect3 = vectl + vect2;
Console.WriteLine("vectl = " + vectl.ToString());
Console.WriteLine("vect2 = " + vect2.ToString());
Console.WriteLine("vect3 = " + vect3.ToString());
}
Сохраним этот код в файле Vectors.cs, скомпилируем и после его запуска получим:
vect1 = (3,3,1)
vect2 = (2 ,-4,-4)
vect3 = (5,-1,-3)
