Г. Шилдт - Полный справочник по C# (1160789), страница 48
Текст из файла (страница 48)
Например, если в строке кодаI i = ( i n t ) a;удалить оператор приведения типов, программа не скомпилируется.Определение и использование операторов преобразования связано с рядом ограничений.• Исходный тип объекта либо тип результата преобразования должен совпадать ссоздаваемым классом. Не разрешается переопределять такие преобразования,как из типа double в тип i n t .• Нельзя определять преобразование в класс Object или из него.• Нельзя задавать как явное, так и неявное преобразование одновременно дляодной и той же пары исходного и результирующего типов.• Нельзя задавать преобразование из базового класса в производный.
(О базовыхи производных классах см. главу 11.)• Нельзя задавать преобразование из одного интерфейса в другой. (Об интерфейсах см. главу 12.)Помимо перечисленных правил существуют также рекомендации, которым обычноследуют при выборе между явным и неявным операторами преобразования. Несмотряна определенные удобства к неявно заданным преобразованиям прибегают только вситуациях, когда преобразование гарантированно лишено ошибок. Другими словами,неявные операторы преобразования следует создавать только при соблюдении следующих условий. Во-первых, такое преобразование должно гарантировать отсутствиепотери данных, которое имеет место при усечении, переполнении или потере знака.Во-вторых, преобразование не должно стать причиной возникновения исключительных ситуаций.
Если предполагаемое преобразование не отвечает этим требованиям,следует использовать преобразование явного типа.Ш Рекомендации и ограничения по созданиюперегруженных операторовДействие перегруженного оператора применительно к классу, для которого он определяется, не обязательно должно иметь отношение к стандартному действию этогооператора применительно к встроенным С#-типам.
Но в целях структурированностии читабельности программного кода создаваемый перегруженный оператор должен повозможности отражать исходное назначение того или иного оператора. Например,оператор " + " , перегруженный для класса ThreeD, концептуально подобен оператору" + " , определенному для целочисленных типов.
Ведь вряд ли есть логика в определении для класса, например, оператора " + " , который по своему действию больше напоминает оператор деления (/). Таким образом, основная идея создания перегруженного оператора — наделить его новыми (нужными для вас) возможностями, которыетем не менее связаны с его первоначальным назначением.250Часть I. Язык С#На перегрузку операторов налагается ряд ограничений.
Нельзя изменять приоритетоператора. Нельзя изменять количество операндов, принимаемых оператором, хотяоператорный метод мог бы игнорировать любой операнд. Некоторые операторы вообще нельзя перефужать. Например, нельзя перефужать какие бы то ни было операторы присваивания (включая составные, например "+="). Ниже перечислены остальные операторы, перефузка которых запрещена.&&| |[]()newissizeoftypeof?->=Несмотря на то что нельзя перефужать оператор приведения типов (()) в явномвиде, можно создать операторы преобразования, которые, как было показано выше,успешно выполняют это.Может показаться серьезным офаничением запрещение перефузки таких составных операторов присваивания, как "+=".
Если вы определили оператор, который используется в составном операторе присваивания, будет вызван соответствующий перегруженный операторный метод. Таким образом, использование "+=" в программеавтоматически вызывает вашу версию метода operator+ (). Например, возьмем сновакласс ThreeD. При использовании фрагмента кодаThreeD a = new ThreeD(l, 2, 3 ) ;ThreeD b = new ThreeD(10, 10, 10);b += a; // Суммируем а и b .Iавтоматически вызывается метод o p e r a t o r + ( ) класса ThreeD, в результате чего объект b будет содержать координаты 11,12,13.И еще. Несмотря на то что нельзя перефужать оператор доступа к элементам массива по индексу ( [ ] ) , используя операторный метод ( o p e r a t o r ()), можно создаватьиндексаторы, которые описаны в следующей главе.Еще один пример перегрузки операторовНа протяжении всей этой главы для демонстрации перефузки операторов мы использовали класс ThreeD.
Но прежде чем завершить эту главу, хотелось бы рассмотреть еще один пример. Несмотря на то что основные принципы перефузки операторов не зависят от используемого класса, следующий пример поможет продемонсфировать мощь перефузки операторов, особенно в случаях, связанных с расширяемостью типов.В этом примере профаммы разрабатывается четырехразрядный целочисленныйтип данных, для которого определяется ряд операций. Возможно, вам известно, чтона заре компьютерной эры четырехразрядный тип был широко распространен, поскольку он представлял половину байта.
Этот тип также позволял хранить одну шестнадцатеричную цифру. Так как четыре бита составляют половину байта, эту полубайтовую величину часто называли nybble. В период всеобщего распространения компьютеров с передней панелью, ввод данных в которые осуществлялся порциями объемомв 1 nybble, профаммисты привыкли оперировать величинами такого размера. Несмотря на то что полубайт нынче утратил былую популярность, этот четырехразрядныйтип представляет интерес в качестве дополнения к другим целочисленным типам данных.
По традиции nybble-значение рассматривается как значение без знака.В следующем примере используется класс Nybble, в котором реализуется полубайтовый тип данных. Он основан на встроенном типе i n t , но офаничивает допустимыедля хранения значения диапазоном 0—15. В этом классе определяются следующиеоператоры:•сложение Nybble-объекта с Nybble-объектом;Глава 9.
Перегрузка операторов251•сложение int-значения с Nybble-объектом;•сложение Nybble-объекта с int-значением;•больше (>) и меньше (<);•оператор инкремента;•преобразование int-значения в Nybble-объект;•преобразование Nybble-объекта в int-значение.Этих операций вполне достаточно, чтобы показать, насколько полно тип классаможет быть интегрирован с системой типов С#.
Однако для полноты реализации типаNybble, необходимо определить остальные операции. Это предлагается сделать читателю в качестве упражнения.Ниже приведен код класса Nybble, а также класса NybbleDemo, который позволяет продемонстрировать его использование.// Создание 4-битового типа с именем Nybble.using System;// 4-битовый тип.class Nybble {int val; // Основа типа - встроенный тип int.public Nybble() { val = 0 ; }public Nybble(int i) {va1 = i;val = val & OxF; // Выделяем младшие 4 бита.}// Перегружаем бинарный оператор " + " для// сложения Nybble + Nybble.public static Nybble operator +(Nybble opl, Nybble op2){Nybble result = new Nybble();result.val = opl.val + op2.val;result.val = result.val & OxF; // Оставляем младшие// 4 бита.return result;}// Перегружаем бинарный оператор "+" для// сложения Nybble + int.public static Nybble operator +(Nybble opl, int op2){Nybble result = new Nybble();result.val = opl.val + op2;result.val = result.val & OxF; // Оставляем младшие// 4 бита.return result;252Часть I.
Язык С#// Перегружаем бинарный оператор "+" для// сложения int + Nybble.public static Nybble operator +(int opl, Nybble op2){Nybble result = new Nybble();result.val = opl + op2.val;result.val = result.val & OxF; // Оставляем младшие// 4 бита.return result;// Перегружаем оператор "++".public static Nybble operator ++(Nybble op){op.val++;op.val = op.val & OxF; // Оставляем младшие// 4 бита,return op;// Перегружаем оператор ">".public static bool operator >(Nybble opl, Nybble op2){if(opl.val > op2.val) return true;else return false;// Перегружаем оператор "<".public static bool operator <(Nybble opl, Nybble op2){if(opl.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 {public static void Main() {Nybble a = new Nybble(1);Nybble b = new Nybble(10);Nybble с = new Nybble();int t;Console.WriteLine("a: " + (int) a ) ;Глава 9.
Перегрузка операторов253Console.WriteLine("b: " + (int) b ) ;// Используем Nybble-объект в if-инструкции,if(a < b) Console.WriteLine("а меньше b\n");// Суммируем два Nybble-объекта.с = a + b;Console.WriteLine("с после сложения с = a + b: " + (int) c ) ;// Суммируем int-значение с Nybble-объектом.a += 5;Console.WriteLine("а после сложения а += 5: " + (int) a ) ;Console.WriteLine();// Используем Nybble-объект в int-выражении,t = a * 2 + 3;Console.WriteLine("Результат выражения а * 2 + 3: " + t) ;Console.WriteLine();// Иллюстрируем присваивание Nybble-объекту// int-значения и переполнение.а = 19;Console.WriteLine("Результат присваивания а = 19: " + (int) a ) ;Console.WriteLine();// Используем Nybble-объект для управления циклом.Console.WriteLine("Управляем for-циклом с помощью Nybble-объекта.");for(а = 0; а < 10; а++)Console.Write((int) a + " " ) ;Console.WriteLine();При выполнении эта профамма генерирует следующие результаты:а: 1Ь: 10а меньше bс после сложения с = а + Ь: 11а после сложения а += 5: бРезультат выражения а * 2 + 3: 15Результат присваивания а = 19: 3Управляем for-циклом с помощью Nybble-объекта.0 1 2 3 4 5 6 7 8 9Несмотря на то что большинство реализованных здесь операций не нуждается вдополнительных комментариях, имеет смысл остановиться вот на чем.
Операторыпреобразования играют немаловажную роль в инте фации типа Nybble в систему ти254Часть I. Язык С #пов С#. Поскольку в этом классе определены преобразования как из Nybble-объектав int-значение, так из int-значения в Nybble-объект, то Nybble-объекты можносвободно смешивать в арифметических выражениях. Рассмотрим, например, такоевыражение из этой программы:J t = а * 2 + 3;Здесь переменная t имеет тип i n t , а переменная а представляет собой объекткласса Nybble. Тем не менее эти два типа совместимы в одном выражении благодаряоператору неявного преобразования Nybble-объекта в int-значение. В данном случае, поскольку остальная часть выражения имеет тип i n t , объект а преобразуется вint-значение посредством вызова соответствующего метода преобразования.Преобразование int-значения в Nybble-объект позволяет присваивать Nybbleобъекту int-значение.