Г. Шилдт - Полный справочник по C# (1160789), страница 39
Текст из файла (страница 39)
f a c t o r y ( i , j ) ; // Создаем// объект.anotherOb.show();Console.WriteLine();Вот результаты выполнения этой программы:а и Ь : 0 10а и Ь: 1 9а и Ь: 2 8а и Ь: 3 7а и Ь: 4 6а и Ь: 5 5а и Ь: 6 4а и Ь: 7 3а и Ь: 8 2а и Ь: 9 1Рассмотрим этот пример более внимательно. В классе MyClass конструктор не определен, поэтому доступен только конструктор, создаваемый средствами С# по умолчанию. Следовательно, установить значения членов класса а и b с помощью конструктора невозможно. Однако создавать объекты с заданными значениями членов а иb способна "фабрика" класса, реализованная в виде метода f a c t o r y (). Более того,поскольку члены а и b закрыты, использование метода f a c t o r y () — единственныйспособ установки этих значений.В функции Main () создается объект ob класса MyClass, а затем в цикле for создается еще десять объектов.
Приведем здесь строку кода, которая представляет собойосновной "конвейер" объектов.1 MyClass anotherOb = o b . f a c t o r y ( i , j ) ; // создаем объектГлава 8. Подробнее о методах и классах201На каждой итерации цикла создается ссылочная переменная anotherOb, которойприсваивается ссылка на объект, сгенерированный "фабрикой" объектов. В концекаждой итерации цикла ссылочная переменная anotherOb выходит из области видимости, и объект, на который она ссылалась, утилизируется.Возвращение методами массивовПоскольку в С# массивы реализованы как объекты, метод может возвратить массив.
(В этом еще одно отличие С# от языка C++, в котором не допускается, чтобыметод, или функция, возвращал массив.) Например, в следующей программе методfindf a c t o r s () возвращает массив, который содержит множители аргумента, пережданного этому методу./У Демонстрация возврата методом массива.using System;class Factor {/* Метод возвращает массив, содержащий множителипараметра num. После выполнения методаout-параметр numfactors будет содержать количествонайденных множителей. */public int[] findfactors(int num, out int numfactors) {int[] facts = new int[80]; // Размер 80 взят произвольно,int i, j ;// Находим множители и помещаем их в массив facts.for(i=2, j=0; i < num/2 + 1;if( (num%i)==0 ) {facts[j] = i;numfactors = j;return facts;class FindFactors {public static void Main() {Factor f = new Factor();int numfactors;int[] factors;,factors = f.findfactors(1000, out numfactors);Console.WriteLine("Множители числа 1000: " ) ;for(int i=0; i < numfactors; i++)Console.Write(factors[i] + " " ) ;Console.WriteLine();1Вот результаты выполнения этой программы:Множители числа 1000:2 4 5 8 10 20 25 40 50 100 125 200 250 500202Часть I.
Язык С#В классе F a c t o r метод findf a c t o r s () объявляется следующим образом:I public int[] findfactors(int num, out int numfactors) {Обратите внимание на то, как задан тип возвращаемого массива i n t . Этот синтаксис можно обобщить. Если вам нужно, чтобы метод возвращал массив, объявите его(метод) подобным образом, изменив при необходимости тип массива и размерность.Например, эта инструкция объявляет метод с именем someMethO, который возвращает двумерный массив double-значений.« p u b l i c d o u b l e t , ] someMethO { // . . .Перегрузка методовВ этом разделе мы узнаем об одной из самых удивительных возможностей языкаС# — перегрузке методов. В С# два или больше методов внутри одного класса могутиметь одинаковое имя, но при условии, что их параметры будут различными.
Такуюситуацию называют перегрузкой методов (method overloading), а методы, которые в нейзадействованы, — перегруженными (overloaded). Перегрузка методов — один из способов реализации полиморфизма в С#.В общем случае для создания перегрузки некоторого метода достаточно объявитьеще одну его версию. Об остальном позаботится компилятор.
Но здесь необходимоотметить одно важное условие: все перегруженные методы должны иметь списки параметров, которые отличаются по типу и/или количеству. Методам для перегрузки недостаточно отличаться лишь типами возвращаемых значений. Они должны отличаться типами или числом параметров. (Другими словами, тип возвращаемого значенияне обеспечивает достаточную информацию для С#, чтобы можно решить, какойименно метод должен быть вызван.) Конечно, перегруженные методы могут отличаться и типами возвращаемых значений. При вызове перегруженного метода выполняется та его версия, параметры которой совпадают (по типу и количеству) с заданными аргументами.Вот простой пример, иллюстрирующий перегрузку методов:// Демонстрация перегрузки методов.using System;class Overload {public void ovlDemo() {Console.WriteLine("Без параметров");}// Перегружаем метод ovlDemo() для одного// целочисленного параметра,public void ovlDemo(int a) {Console.WriteLine("Один параметр: " + a ) ;}// Перегружаем метод ovlDemo() для двух// целочисленных параметров,public int ovlDemo(int a, int b) {Console.WriteLine("Два int-параметра: " + a + " " + b ) ;return a + b;// Перегружаем метод ovlDemo() для двухГлава 8.
Подробнее о методах и классах203// double-параметров.public double ovlDemo(double a, double b) {Console.WriteLine("Два double-параметра: " +a + " "+ b ) ;return a + b;class OverloadDemo {public static void Main() {Overload ob = new OverloadO;int resl;double resD;// Вызываем все версии метода ovlDemo().ob.ovlDemo();Console.WriteLine();ob.ovlDemo(2);Console.WriteLine();resl = ob.ovlDemo(4, 6 ) ;Console.WriteLine("Результат вызова ob.ovlDemo(4, 6 ) :+ resl);Console.WriteLine();resD = ob.ovlDemo(1.1, 2.32);Console.WriteLine("Результат вызова ob.ovlDemo(1.1, 2.2):resD);Программа генерирует следующие результаты:Без параметровОдин параметр: 2Два int-параметра: 4 бРезультат вызова ob.ovlDemo(4, б ) : 10Два double-параметра: 1.1 2.32Результат вызова ob.ovlDemo(I.1, 2.2): 3.42Как видите, метод ovlDemo () перегружается четыре раза.
Первая версия вообщене принимает параметров, вторая принимает один целочисленный параметр, третья —два целочисленных параметра, а четвертая — два double-параметра. Обратите внимание на то, что первые две версии метода ovlDemo () возвращают тип void, т.е.
невозвращают никакого значения, а вторые две возвращают значения соответствующихтипов. Это вполне допустимо, но, как уже разъяснялось, перегрузка методов не достигается различием только в типе возвращаемого значения. Поэтрму попытка использовать следующие две версии метода ovlDemo () приведет к ошибке:// Одно объявление метода ovlDemo(int) вполне допустимо,public void ovlDemo(int a) {Console.WriteLine("Один параметр: " + a ) ;204Часть I. Язык С#// Ошибка! Два объявления метода ovlDemo(int) неприемлемы,// несмотря на т о , что типы возвращаемых ими значений// разные,p u b l i c i n t ovlDemo(int a) {Console.WriteLine("Один параметр: " + a ) ;r e t u r n a * a;}Как отмечено в комментариях, различие в типах значений, возвращаемых методами, является недостаточным фактором для обеспечения их перегрузки.Как указывалось в главе 3, в определенных пределах С# обеспечивает автоматическое преобразование типов.
Эта возможность преобразования типов применяется и кпараметрам перегруженных методов. Рассмотрим, например, следующую программу./* Возможность автоматического преобразования типов можетповлиять на решение о перегрузке методов. */using System;class 0verload2 {public void f ( i n t x) {Console.WriteLine("Внутри метода f ( i n t ) :p u b l i c void f(double x) {Console.WriteLine("Внутри" + x);метода f ( d o u b l e ) :" + x)class TypeConv {public static void Main() {Overload2 ob = new 0verload2();int i = 10;double d = 10.1;b y t e b = 99;s h o r t s = 10;f l o a t f = 11.5F;ob.f(i); // Вызов метода ob.f(int).ob.f(d); // Вызов метода ob.f(double).ob.f(b); // Вызов метода ob.f(int) — выполняется// преобразование типов,ob.f(s); // Вызов метода ob.f(int) — выполняется// преобразование типов,ob.f(f); // Вызов метода ob.f(double) — выполняется// преобразование типов.Вот результатыВнутри методаВнутри методаВнутри методаВнутри методаВнутри методавыполнения этой программы:f(int): 10f(double): 10.1f(int): 99f(int): 10f(double): 11.5Глава 8.
Подробнее о методах и классах205В этом примере определены только две версии метода f (): одна с i n t - , а другая сdouble-параметром. Тем не менее методу f () можно передать помимо значений типаi n t и double также значения типа byte, s h o r t или f l o a t . В случае передачи byteили short-параметров С# автоматически преобразует их в значения типа i n t (т.е. будет вызвана версия f ( i n t ) ) .
В случае передачи float-параметра его значение будетпреобразовано в значение типа double и будет вызвана версия f (double).Здесь важно понимать, что автоматическое преобразование применяется только втом случае, когда не существует прямого соответствия параметра и аргумента. Дополним предыдущую программу версией метода f ( ) , в которой определен параметр типа_byte.// Добавление к предыдущей программе версии f(byte).using System;class 0verload2 {public void f(byte x) {Console.WriteLine("Внутри метода f(byte): " + x ) ;}public void f(int x) {Console.WriteLine("Внутри метода f(int): " + x) ;}public void f(double x) {Console.WriteLine("Внутри метода f(double): " + x) ;class TypeConv {public static void Main() {0verload2 ob = new 0verload2();int i = 10;double d = 10.1;byte b = 99;short s = 10;float f = 11.5F;ob.f(i); // Вызов метода ob.f(int).ob.f(d); // Вызов метода ob.f(double).ob.f(b); // Вызов метода ob.f(byte) - теперь без// преобразования типов.ob.f(s); ////ob.f(f); ////Вызов метода ob.f(int) — выполняетсяпреобразование типов,Вызов метода ob.f(double) — выполняетсяпреобразование типов.Этот вариант программы генерирует такие результаты:Внутри метода f(int): 10Внутри метода f(double): 10.1(206Часть I.
Язык С#•Внутри метода f ( b y t e ) : 99Внутри метода f ( i n t ) : 10Внутри метода f ( d o u b l e ) : 11.5В этом варианте, поскольку существует версия метода f (), которая предназначенадля приема аргумента типа byte, при вызове метода f () с byte-аргументом будет вызвана версия f (byte), и автоматического преобразования byte-аргумента в значениетипа i n t не произойдет.Наличие как ref-, так и out-модификатора играет роль в "зачете" перегруженных>ункций. Например, в следующем фрагменте кода определяются два различных метода.p u b l i c void f ( i n t x) {Console.WriteLine("Внутри метода f(int): " + x ) ;}public void f(ref int x) {Console.WriteLine("Внутри метода f(ref int): " + x ) ;}Таким образом, при выполнении инструкцииob.f(i);вызывается метод f ( i n t x), но при выполнении инструкции| ob.f(ref i ) ;вызывается метод f (ref i n t x).Посредством перегрузки методов в С# поддерживается полиморфизм, посколькуэто единственный способ реализации в С# парадигмы "один интерфейс — множествометодов".
Чтобы понять, как это происходит, рассмотрим следующее. В языке, который не поддерживает перегрузку методов, каждый метод должен иметь уникальноеимя. Однако часто нужно реализовать один и тот же метод для различных типов данных. Возьмем, например, функцию, возвращающую абсолютное значение. В языках,которые не поддерживают перегрузку методов, обычно существует три или дажебольше версий этой функции, причем их имена незначительно отличаются. Например, в языке С функция abs () возвращает абсолютное значение (модуль) целого числа, функция l a b s () возвращает модуль длинного целочисленного значения, аf abs () — модуль значения с плавающей точкой.