Г. Шилдт - С# 3.0 Полное руководство. 2010 (1160798), страница 46
Текст из файла (страница 46)
)лава 8, Подробнее о методах и классах 231 с1аяя Очег1оаб2 ( рпЫ1с чогб Мумегп(Ьуге х) ( сопяо1е.игтгеьапе("В методе мумегь(ьуге): " + х)т ) рпЫхс чотд Мумегп (1пг х) ( Сопяо1е.нг1гевапе ("В методе Иумегп (1пг): " + х) т риЫ1с чогб МумегЬ(бопЫе х) ( Сопяо1е.игггегхпе ("В методе Мумегп ШопЫе) т " + х) и 1 с1аяя туреСопч ( ягаг1с чо16 Иа1п() ( Очег1оаб2 оЬ = пеи Очег1оаб2() 1пг 1 = 10Х бопЫе б = 10.1х Ьуге Ь = 99; япогг я = 10) Г1оаг Г = 11.5Г) оЬ.мумегЬ (1) х О вызвать метод оЬ .Мумегп(1пг) оЬ.мумегь Ш)т // вызвать метод оь.мумегь(босые) оЬ.мумегЬ(Ы ) // вызвать метод оЬ.мумегЬ(Ьуге) // на этот раэ без преобразования типа Выполнение этой программы приводит к следующему результату: В методе МумегЬ(1пг): 10 В методе Мунегп(бопЫе): 10.1 В методе МуМегп(Ьуге)т 99 В методе МуМегЬ(тпг): 10 В методе Мумегп(бопЫе): 11.5 В этой программе присутствует вариант метода мумегь (), принимающий аргумент типа ьуге, поэтому при вызове данного метода с аргументом типа ьуге выбирается его вариант Мумегь (Ьуге) без автоматического преобразования в тип хпг.
Оба модификатора параметров, ген и оог, также учитываются, когда принимается решение о перегрузке метода. В качестве примера ниже приведен фрагмент кода, в котором определяются два совершенно разных метода. рпЫхс чохи МумегЬ(хпг х) ( Сопяо1е.нг1гепдпе("Внутри метода МумегЬ(1пЦ: " + х)т ) оЬ.Иумегп (я) ) // оЬ.Муивгп(Г)) // // вызвать метод оЬ.Иумегп(хпг) с преобразованием типа // вызвать метод оЬ.мумегп(бопЬ1е) с преобразованием типа 232 часть ). Взык с№ рпЬ11с чо1к( МуМесп(гет №пс х) ( Сопао1е.игхсеьдпе("Внутри метода Мумесь(гет ьпс): " + х); ) Следовательно, при обрашении оЬ.МуМесп(1) вызывается метод мумегь (дпс х), но при обращении оЬ.Мумесп(гег 1) вызывается метод мумегь(гег №пс х).
Несмотря на то что модификаторы параметров ге№ и опс учитываются, когда принимается решение о перегрузке метода, отличие между ними не столь существенно. Например, два следующих варианта метода Мумегп () оказываются недействительными: // Оиибха! роьагс чогц Мумесп(оис №пк х) ( // роЬ1№с чо1к( Мумесп(гет 1пс х) ( // В данном случае компилятор не в состоянии различить два варианта одного и того же метода мумегь () только на основании того, что в одном из них используется параметр оог,а в другом — параметр ген.
Перегрузка методов поддерживает свойство полиморфизма, поскольку именно таким способом в С№ реализуется главный принцип полиморфизма: один интерфейс — множество методов. Для того чтобы стало понятнее, как это делается, обратимся к конкретному примеру. В языках программирования, не поддерживающих перегрузку методов, каждому методу должно быть присвоено уникальное имя. Но в программировании зачастую возникает потребность реализовать по сути один и тот же метод для обработки разных типов данных. Допустим, что требуется функция, определяющая абсолютное значение.
В языках, не поддерживающих перегрузку методов, обычно приходится создавать три или более вариантов такой функции с несколько отличающимися, но все же разными именами. Например, в С функция аьэ () возврашает абсолютное значение целого числа, функция 1аЬз () — абсолютное значение длинного целого числа, а функция гаЬВ () — абсолютное значение числа с плавающей точкой обычной (одинарной) точности.
В языке С перегрузка не поддерживается, и поэтому у каждой функции должно быть свое, особое имя, несмотря на то, что все упомянутые выше функции, по существу, делают одно и то же — определяют абсолютное значение. Но это принципиально усложняет положение, поскольку приходится помнить имена всех трех функций, хотя они реализованы по одному и тому же основному принципу. Подобные затруднения в языке С№ не возникают, поскольку каждому методу, определяюшему абсолютное значение, может быть присвоено одно и то же имя.
И действительно, в состав библиотеки классов среды .)к)ГТ ггашеьчог)г входит метод ВЬз (), который перегружается в классе Зузсем. Маей для обработки данных разных числовых типов. Компилятор С№ сам определяет, какой именно вариант метода АЬВ () следует вызывать, исходя из типа передаваемого аргумента. Главная ценность перегрузки заключается в том, что она обеспечивает доступ к связанным вместе методам по обшему имени. Следовательно, имя ВЬВ обозначает общее выполняемое действие, а компилятор сам выбирает конкретный вариант метода по обстоятельствам. Благодаря полиморфизму несколько имен сводятся к одному. Несмотря на всю простоту рассматриваемого здесь примера, продемонстрированный в нем принцип полиморфизма можно расширить, чтобы выяснить, каким образом перегрузка помогает справляться с намного более сложными ситуациями в программировании. Глава б.
Подробнее о методах н классах 233 Когда метод перегружается, каждый его вариант может выполнять какое угодно действие. Для установления взаимосвязи между перегружаемыми методами не существует какого-то твердого правила, но с точки зрения правильного стиля программирования перегрузка методов подразумевает подобную взаимосвязь. Следовательно, использовать одно и то же имя для несвязанных друг с другом методов не следует, хотя зто и возможно. Например, имя Вс(г можно было бы выбрать для методов, возвращающих квадрат и квадратный корень числа с плавающей точкой. Но ведь зто принципиально разные операции. Такое применение перегрузки методов противоречит ее первоначальному назначению.
На практике перегружать следует только тесно связанные операции. В языке С№ определено понятие сигнатуры, обозначающее имя метода и список его параметров. Применительно к перегрузке это понятие означает, что в одном классе не должно существовать двух методов с одной и той же сигнатурой. Следует подчеркнуть, что в сигнатуру не входит тип возвращаемого значения, поскольку он не учитывается, когда компилятор С№ принимает решение о перегрузке метода. В сигнатуру ие входит также модификатор рагап[в. Перегрузка конструкторов Как и методы, конструкторы также могут перегружаться.
Это дает возможность конструировать объекты самыми разными способами. В качестве примера рассмотрим следующую программу: // Продемонстрировать перегрузку конструктора. пвтпд Зувкеит с1аяв МуС1явв ( рпьгтс 1пя хт рпп11с мус1авв() (сопво1е.нк1сеь1пе("В конструкторе мус1вяв[).")т х о) ) рпъ11с МуС1авв(1пк 1) ( Сопво1е. Уктгеьтпе ( "В конструкторе МуС1авв(тпк) .") т х = Ьт ) рпп11с Мус1явв(аопЬ1е а) ( Сопво1е.ис1кетипе("В конструкторе МуС1аяв(попые).")т х = (1пк) Ф ) рпь11с МуС1авв(1пк х, Ьпе б) ( Сопво1е.иггкедапе("В конструкторе Мус1явв(1пт, 1пк).")т х = 1 * бт 234 Часть (, Язык СЯ с1авв Очея1оат(сопввешо ( якаеьс чохб Мвтп() ( МуС1аяя С1 пен МуС1аяя() т МуС1аяв С2 = пен МуС1авв (88) т МуС1аяя СЗ = пеи МуС1аяя(17.23) т МуС1авв С4 = пен МуС1авв(2, 4)т Сопзо1е.ит1ееьгпе("с1.хк " + С1.х)( Сопво1е.нтткеъгпе("Сг.хь " + С2.х)т Сопво1е.нттееьтпе("СЗ.хь " + СЗ.х)т Сопяо1е.нт1кеьгпе("С4.х: " + С4.х)т ) При выполнении этой программы получается следующий результат: В конструкторе МуС1аяя().
В конструкторе Мус1аяя(1пт). В конструкторе Мус1аяв(бопп1е). В конструкторе МуС1аяя (Япс, ъпс) . С1.хк О Сг.хь 88 сЗ.х: 17 Ск.хк 8 В данном примере конструктор мус1азв() перегружается четыре раза, всякий раз конструируя объект по-разному. Подходящий конструктор вызывается каждый раз, исходя из аргументов, указываемых при выполнении оператора пеи. Перегрузка конструктора класса предоставляет пользователю этого класса дополнительные удобства в конструировании объектов.
Одна из самых распространенных причин для перегрузки конструкторов заключается в необходимости предоставить возможность одним объектам инициализировать другие. В качестве примера ниже приведен усовершенствованный вариант разработанного ранее класса Ятасх, позволяющий конструировать один стек из другого.
// Класс для хранения символов в стеке. пя1пч Яуякеяы с1аяя Яеаск ( // Эти члены класса являются закрытыми. сьат() якскт // массив, содержаший стек 1пс Соят // индекс вершины стека // Сконструировать пустой объект класса Якаск // по заданному размеру стека. рпъзьс Якаск(ъпк втхе) ( яссх = пен сьат[яъяе); // распределить память для стека Соя = О; ) // Сконструировать объект класса Якасх из // существующего стека. Глава 8, Подробнее о методах н классах 236 роь]хс Вгаск(вгаск оь) ( // Распределить память для стека. вссК пен снах[ой.зссК.Ьепесй]; // Скопировать элементы в новьп» стек.
ГОГ(1пг 1=От 1 < ОЬ.Г05) 1++) вгсК[1] = ОЬ.згсК[1]т // установить переменную гоэ для нового стека. Гоэ = ОЬ.Говт ) // Поместить символы в стек. рпй11с чойб Ровй(сйаг сй) ( 11(Г05==5ссК.Ьепдсй) [ Сопво1е.щг1сеЬхпе(" — Стек заполнен.") гегигпт ) 5гсК[ГО5] = сйт СО5Н.) ) // Извлечь символ из стека. рий11с сйаг Рор() ( 11(Г05==0) ( Сопзо1е.мг]сеЬ]пе(" — Стек пуст."); гегпгп (сйаг) 0; ) ГО5 —; гесогп 5ссК[сов] // Возвратить значение ГгОЕ, ЕСЛИ стек заполнен. ройахс Ьоо1 15РО11() ( гесогп Гоэ==вссК.ЬепССЬ) ) // Возвратить значение Ггое, если стек пуст.