Г. Шилдт - Полный справочник по C# (1160789), страница 40
Текст из файла (страница 40)
Поскольку язык С не поддерживаетперегрузку методов, каждая функция должна иметь собственное имя, несмотря на то,что все три функции выполняют по сути одно и то же действие. Это делает ситуациюсложнее, чем она есть на самом деле. Другими словами, при одних и тех же действияхпрограммисту необходимо помнить имена всех трех (в данном случае) функций.
ЯзыкС# избавлен от ненужного "размножения" имен, поскольку все методы получения абсолютного значения могут использовать одно и то же имя. И в самом деле, библиотека стандартных классов С# включает метод получения абсолютного значения с именем Abs (). Этот метод перегружается С#-классом System.Math, что позволяет обрабатывать значения всех числовых типов, используя одно имя метода. Определениетого, какая именно версия метода должна быть вызвана, основано на типе передаваемого аргумента.Принципиальная значимость перегрузки состоит в том, что она позволяет обращаться к связанным методам посредством одного, общего для всех имени.
Следовательно, имя Abs () представляет общее действие, которое выполняется во всех случаях.Компилятору остается правильно выбрать конкретную версию при конкретных обстоятельствах. А программисту нужно помнить лишь общую операцию, которая связана с именем того или иного метода. Благодаря полиморфизму применение нескольких имен сводится к одному. Несмотря на простоту приведенного примера, он все жепозволяет понять, что перегрузка способна упростить процесс программирования.Глава 8, Подробнее о методах и классах207Необходимо подчеркнуть, что каждая версия перефуженного метода может выполнять определенные действия.
Не существует правила, которое бы обязывало профаммиста связывать перефуженные методы общими действиями. Однако с точкизрения стилистики перефузка методов все-таки подразумевает определенное"родство" его версий. Таким образом, несмотря на то, что вы можете использоватьодно и то же имя для перефузки не связанных общими действиями методов, этогоделать не стоит.
Например, в принципе можно использовать имя s q r для созданияметода, который возвращает квадрат целого числа, и метода, который возвращает значение квадратного корня из вещественного числа. Но поскольку эти операции фундаментально различны, применение механизма перефузки методов в этом случае сводитна нет его первоначальную цель. Хороший стиль профаммирования состоит в организации перефузки тесно связанных операций.В С# используется термин сигнатура (signature), который представляет собой имяметода со списком его параметров.
Таким образом, в целях обеспечения перефузкиникакие два метода внутри одного и того же класса не должны иметь одинаковуюсигнатуру. Обратите внимание на то, что сигнатура не включает тип значения, возвращаемого методом, поскольку этот фактор не используется в С# для принятия решения о выполнении требуемого перефуженного метода. Сигнатура также не включает pa rams-параметр, если таковой существует. Другими словами, модификаторparams не является определяющим фактором отличия одного перефуженного методаот другого.Перегрузка конструкторовПодобно другим методам, конструкторы также можно перефужать. Это позволяетсоздавать объекты различными способами.
Рассмотрим следующую программу:// Демонстрация перегруженных конструкторов.using System;class MyClass {public int x;public MyClass() {Console.WriteLine("Внутри конструктора MyClass().");x = 0;p u b l i c MyClass(int i) {Console.WriteLine("Внутриx = i;конструктора M y C l a s s ( i n t ) . " ) ;public MyClass(double d) {Console.WriteLine("Внутри конструктора MyClass(double).");x = (int) d;}public MyClass(int i, int j) {Console.WriteLine("Внутри конструктора MyClass(int, int).");x = i * j;208Часть I. Язык С#class OverloadConsDemo {public static void Main() {MyClass tl = new MyClassO;MyClass t2 = new MyClass(88);MyClass t3 = new MyClass(17.23);MyClass t4 = new MyClass(2, 4 ) ;Console.WriteLine("tl.x:Console.WriteLine("t2.x:Console.WriteLine("t3.x:Console.WriteLine("t4.x:""""++++tl.x);t2.x);t3.x);t4.x);При выполнении этой программы получаем следующие результаты:Внутри конструктораВнутри конструктораВнутри конструктораВнутри конструктораtl.x: 0t2.x: 88t3.x: 17t4.x: 8MyClassO.MyClass(int).MyClass(double).MyClass(int, int).•Конструктор MyClass () перегружен четырежды, и все конструкторы создают объекты по-разному.
В зависимости от того, какие параметры заданы при выполненииоператора new, вызывается соответствующий конструктор. Перегружая конструкторкласса, вы тем самым предоставляете пользователю этого класса определенную гибкость в выборе способа создания объектов.Одна из самых распространенных причин перегрузки конструкторов — возможность инициализации одного объекта с помощью другого.
Например, вот как выглядит усовершенствованная версия представленного выше класса s t a c k , которая позволяет создать один стек на основе другого:// Класс стека для хранения символов.u s i n g System;class Stack {// Эти члены закрыты.char[] stck; // Этот массив содержит стек.int tos;// Индекс вершины стека.// Создаем пустой объект класса Stack заданного размера,public Stack(int size) {stck = new char[size]; // Выделяем память для стека.tos = 0;// Создаем Stack-объект на основе существующего стека,public Stack(Stack ob) {// Выделяем память для стека.stck = new char[ob.stck.Length];// Копируем элементы в новый стек,for(int i=0; i < ob.tos;Глава 8.
Подробнее о методах и классах209stckfi] = ob.stckfi];// Устанавливаем переменную tos для нового стека,tos = ob.tos;// Помещаем символ в стек.public void push(char ch) {if(tos==stck.Length) {Console.WriteLine(" — Стек заполнен.");return;stck[tos] = ch;tos++;// Извлекаем символ из стека,public char pop() {if(tos==0) {Console.WriteLine(" — Стек пуст.");return (char) 0;tos—;return stckftos];// Метод возвращает значение true, если стек заполнен,public bool full () {return tos—stck.
Length;// Метод возвращает значение true, если стек пуст,public bool empty() {return tos==0;// Возвращает общий объем стека,public int capacity() {return stck.Length;// Возвращает текущее количество объектов в стеке,public int getNum() {return tos;// Демонстрация использования класса Stack,class StackDemo {public static void Main() {Stack stkl = new Stack(10);char ch;int i ;// Помещаем символы в стек stkl.Console.WriteLine(210Часть I. Язык С#"Помещаем символы от А до Z в стек stkl.");for(i=0; !stkl.full(); i++)stkl.push((char) (fAf + i));// Создаем копию стека stckl.Stack stk2 = new Stack(stkl);// Отображаем содержимое стека stkl.Console.Write("Содержимое стека stkl: " ) ;while( !stkl.empty() ) {ch - stkl.pop();Console.Write(ch);Console.WriteLine();Console.Write("Содержимое стека stk2: ")while ( !stk2.empty() ) {ch = stk2.pop();Console.Write(ch);Console.WriteLine("\n");(Результаты выполнения этой программы:Помещаем символы от А до Z в стек s t k l .Содержимое стека s t k l : JIHGFEDCBAСодержимое стека s t k 2 : JIHGFEDCBAВ классе StackDemo создается пустым первый стек s t k l , который заполняетсясимволами.
Этот стек затем используется для создания второго стека s t k 2 , и в этомслучае вызывается следующий конструктор класса stack.// Создаем Stack-объект из существующего стека,public Stack(Stack ob) {// Выделяем память для стека,stck = new char[ob.stck.Length];// Копируем элементы в новый стек.for(int i=0; i < ob.tos; i++)stck[i] = o b . s t c k [ i ] ;// Устанавливаем переменную tos для нового стека,tos = ob.tos;При выполнении кода этого конструктора для массива s t c k выделяется областьпамяти, причем ее размер позволяет поместить в этот массив все элементы, содержащиеся в стеке, заданном в качестве аргумента ob. Затем содержимое базового массива,на котором основан стек ob, копируется в новый массив, и соответствующим образомустанавливается переменная индекса t o s .
По завершении работы этого конструкторановый и исходный стеки являются отдельными объектами, но идентичны по своемусодержимому.Глава 8. Подробнее о методах и классах211Вызов перегруженного конструктора с помощью ссылки thisПри работе с перефуженными конструкторами иногда необходимо обеспечить вызов одного конструктора из другого. В С# это реализуется с помощью еще однойформы ключевого слова t h i s . Общий формат записи такого вызова:имя_конструктора (список__параметров1) :t h i s (список__параметров2) {/I .
. . Тело конструктора,// которое может быть пустым.}При выполнении перегруженного конструктора сначала вызывается та его версия,список параметров которой совпадает с элементом список_параметров2. При этомбудут выполнены любые инструкции, содержащиеся внутри исходного конструктора.Например:// Демонстрация вызова конструктора с помощью ссылки this.using System;class XYCoord {public int x, y;public XYCoord() : this(0, 0) {Console.WriteLine ("Внутри конструктора XYCoordO");}public XYCoord(XYCoord obj) : this(obj.x, obj.y) {Console.WriteLine("Внутри конструктора XYCoord(obj)");}public XYCoord(int i, int j) {Console.WriteLine("Внутри конструктора XYCoord(int, int)");x = i;у = j;class OverloadConsDemo {public static void Main() {XYCoord tl = new XYCoordO;XYCoord t2 = new XYCoord(8, 9 ) ;XYCoord t3 = new XYCoord(t2);Console.WriteLine("tl.x, tl.y: " + tl.x + ", " + tl.y);Console.WriteLine("t2.x, t2.y: " + t2.x + ", " + t2.y);Console.WriteLine("t3.x, t3.y: " + t3.x + ", " + t3.y);Эта программа генерирует следующиеВнутри конструктора XYCoord(int,Внутри конструктора XYCoord()Внутри конструктора XYCoord(int,Внутри конструктора XYCoord(int,Внутри конструктора XYCoord(obj)tl.x, tl.y: 0, 0t2.x, t2.y: 8, 9t3.x, t3.y: 8, 9212результаты:int)int)int)Часть I.
Язык С#Вот как работает эта программа. В классе XYCoord единственным конструктором,который реально инициализирует члены х и у, является XYCoord ( i n t , i n t ) . Остальные два конструктора просто вызывают конструктор XYCoord ( i n t , i n t ) , используяключевое слово t h i s .