Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
Нельзя указывать преобразование в класс object или же из этого класса.
Для одних и тех же исходных и целевых типов данных нельзя указывать одновременно явное и неявное преобразование.
Нельзя указывать преобразование базового класса в производный класс. (Подробнее о базовых и производных классах речь пойдет в главе 11.)
Нельзя указывать преобразование в интерфейс или же из него. (Подробнее об интерфейсах — в главе 12.)
Помимо указанных выше ограничений, имеется ряд рекомендаций, которымиобычно руководствуются при выборе операторов явного или неявного преобразования. Несмотря на все преимущества неявных преобразований, к ним следует прибегать только в тех случаях, когда преобразованию не свойственны ошибки. Во избежание подобных ошибок неявные преобразования должны быть организованы тольков том случае, если удовлетворяются следующие условия. Во-первых, информация нетеряется, например, в результате усечения, переполнения или потери знака. И во-вторых, преобразование не приводит к исключительной ситуации. Если же неявноепреобразование не удовлетворяет этим двум условиям, то следует выбрать явное преобразование.Рекомендации и ограничения по перегрузке операторов
Действие перегружаемого оператора распространяется на класс, для которого онопределяется, и никак не связано с его первоначальным применением к данным встроенных в C# типов. Но ради сохранения ясности структуры и удобочитаемости исходного кода перегружаемый оператор должен, по возможности, отражать основную сутьсвоего первоначального назначения. Например, назначение оператора + для классаThreeD по сути не должно заметно отличаться от его назначения для целочисленныхтипов данных. Если бы, например, определить оператор + относительно некоторогокласса таким образом, чтобы по своему действию он стал больше похожим на оператор /, то вряд ли от этого было бы много проку. Главный принцип перегрузки операторов заключается в следующем: несмотря на то, что перегружаемый оператор можетполучить любое назначение, ради ясности новое его назначение должно быть так илииначе связано с его первоначальным назначением.
На перегрузку операторов накладывается ряд ограничений. В частности, нельзя изменять приоритет любого оператора или количество операндов, которое требуетсядля оператора, хотя в операторном методе можно и проигнорировать операнд. Крометого, имеется ряд операторов, которые нельзя перегружать. А самое главное, что перегрузке не подлежит ни один из операторов присваивания, в том числе и составные,как, например, оператор +=. Ниже перечислены операторы, которые нельзя перегружать. Среди них имеются и такие операторы, которые будут рассматриваться далее вэтой книге.&&().???[]||==>->ascheckeddefaultisnewsizeoftypeofunchecked
Несмотря на то что оператор приведения () нельзя перегружать явным образом,имеется все же возможность создать упоминавшиеся ранее операторы преобразования, выполняющие ту же самую функцию.
Ограничение, связанное с тем, что некоторые операторы, например +=, нельзяперегружать, на самом деле не является таким уж непреодолимым. Вообще говоря,если оператор определен как перегружаемый и используется в составном оператореприсваивания, то обычно вызывается метод этого перегружаемого оператора. Следовательно, при обращении к оператору += в программе автоматически вызывается заранее объявленный вариант метода operator+(). Например, в приведенном нижефрагменте кода метод operator+() автоматически вызывается для класса ThreeD,а в итоге объект b будет содержать координаты 11, 12, 13.ThreeD а = new ThreeD(l, 2, 3);ThreeD b = new ThreeD(10, 10, 10);b += a; // сложить координаты точек а и b
И последнее замечание: несмотря на то, что оператор индексации массива [] нельзя перегружать с помощью операторного метода, имеется возможность создать индексаторы, о которых речь пойдет в следующей главе.Еще один пример перегрузки операторов
Во всех предыдущих примерах программ, представленных в этой главе, для демонстрации перегрузки операторов использовался класс ThreeD, и этой цели он служилисправно. Но прежде чем завершить эту главу, было бы уместно рассмотреть еще одинпример перегрузки операторов. Общие принципы перегрузки операторов остаютсянеизменными независимо от применяемого класса, тем не менее, в рассматриваемомниже примере наглядно демонстрируются сильные стороны такой перегрузки, особенно если это касается расширяемости типов.
В данном примере разрабатывается 4-разрядный целочисленный тип данных идля него определяется ряд операций. Вам, вероятно, известно, что на ранней стадииразвития вычислительной техники широко применялся тип данных для обозначения4-разрядных двоичных величин, называвшихся полубайтами, поскольку они составляли половину байта, содержали одну шестнадцатеричную цифру и были удобны дляввода кода полубайтами с пульта ЭВМ, что в те времена считалось привычным занятием для программистов! В наше время этот тип данных применяется редко, но онпо-прежнему является любопытным дополнением целочисленных типов данных в С#.По традиции полубайт обозначает целое значение без знака.
В приведенном ниже примере программы тип полубайтовых данных реализуется спомощью класса Nybble. В качестве базового для него используется тип int, но с ограничением на хранение данных от 0 до 15. В классе Nybble определяются следующиеоператоры.
Сложение двух объектов типа Nybble.
Сложение значения типа int с объектом типа Nybble.
Сложение объекта типа Nybble со значением типа int.
Операции сравнения: больше (>) и меньше (<).
Операция инкремента.
Преобразование значения типа int в объект типа Nybble.
Преобразование объекта типа Nybble в значение типа int.*Перечисленных выше операций достаточно, чтобы показать, каким образом типкласса Nybble интегрируется в систему типов С#. Но для полноценной реализацииэтого типа данных придется определить все остальные доступные для него операции.Попробуйте сделать это сами в качестве упражнения.
Ниже полностью приводится класс Nybble, а также класс NybbleDemo, демонстрирующий его применение.// Создать полубайтовый тип 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 NybbleDemo { static void Main() { Nybble a = new Nybble(1); Nybble b = new Nybble(10); Nybble с = new Nybble(); int t; Console.WriteLine("a: " + (int) a); Console.WriteLine("b: " + (int) b); // Использовать тип Nybble в условном операторе if. if(а < b) Console.WriteLine("а меньше bn"); // Сложить два объекта типа Nybble. с = а + b; Console.WriteLine("с после операции с = а + b: " + (int) с); // Сложить значение типа int с объектом типа Nybble. а += 5; Console.WriteLine("а после операции а += 5: " + (int) а); Console.WriteLine(); // Использовать тип Nybble в выражении типа int. t = а * 2 + 3; Console.WriteLine("Результат вычисления выражения а * 2 + 3: " + t); Console.WriteLine(); // Продемонстрировать присваивание значения типа int и переполнение. а = 19; Console.WriteLine("Результат присваивания а = 19: " + (int) а); Console.WriteLine(); // Использовать тип Nybble для управления циклом. Console.WriteLine("Управление циклом for " + "с помощью объекта типа Nybble."); for(а = 0; а < 10; а++) Console.Write((int) а + " "); Console.WriteLine(); }}