1629295403-b876e2087bddebea4bc9666fb2377a02 (846199), страница 30
Текст из файла (страница 30)
Вместо этого С# отслеживает переменные при вызове функции — например, вот такой вызов функции приведет к сообщению об ошибке:int n U n i n i t i a l i z e d V a r i a b l e ;SomeFunction(ref n U n i n i t i a l i z e d V a r i a b l e ) ;Если бы C# позволил осуществить такой вызов, то функция S o m e F u n c t i o n () получила бы ссылку на неинициализированную переменную (т.е. на мусор ( g a r b a g e ) ) в памяти. Ключевое слово o u t позволяет функции и вызывающему ее коду договоритьсяо том, что передаваемая по ссылке переменная может быть не инициализирована —функция обещает не использовать ее значение до тех пор, пока оно не будет какимлибо образом присвоено самой функцией.
Следующий фрагмент исходного текстакомпилируется без каких-либо замечаний:int n U n i n i t i a l i z e d V a r i a b l e ;SomeFunction(out n U n i n i t i a l i z e d V a r i a b l e ) ;Передача инициализированной переменной как o u t - а р г у м е н т а также является корректным действием:int n l n i t i a l i z e d V a r i a b l e = 1;SomeFunction(out n l n i t i a l i z e d V a r i a b l e ) ;В этом случае значение переменной n l n i t i a l i z e d V a r i a b l e будет просто проигнорировано функцией S o m e F u n c t i o n ( ) , но никакой опасности для программы этоне представляет.Глава 7. Функции функций161Многие реальные операции создают некоторые значения, которые должны быть возвращены тому, кто вызвал эту операцию. Например, функция s i n () получает аргументи возвращает значение тригонометрической функции "синус" для данного аргументФункция может вернуть значение вызывающей функции двумя способами.
Наиболеераспространенный — с помощью команды r e t u r n ; второй способ использует возможности передачи аргументов по ссылке.Возврат значения оператором returnПриведенный далее фрагмент исходного текста демонстрирует небольшую функциювозвращающую среднее значение переданных ей аргументов,publicclassExample{publicstaticdoubleAverage(doubledl,doubled2){double dAverage =returndAverage;(dl+d2)/2;}publicstaticvoidTestO{d o u b l e vl = 1.0;d o u b l e v2 = 3 .
0 ;double dAverageValue = Average(vl, v2);C o n s o l e . W r i t e L i n e ( " С р е д н е е д л я " + vl+ " и " + v2 + " р а в н о "+ dAverageValue);// Такой метод также вполне работоспособенC o n s o l e . W r i t e L i n e ( " С р е д н е е д л я " + vl+ " и " + v2 + " р а в н о "+ Average(vl, v2));}}Прежде всего обратите внимание, что функция объявлена как p u b l i c s t a t i c doubleA v e r a g e () — тип d o u b l e перед именем функции указывает на тот факт, что функция Ave r a g e () возвращает вызывающей функции значение типа d o u b l e .Функция A v e r a g e () использует имена dl и d2 для значений, переданных ей в качестве аргументов.
Она создает переменную d A v e r a g e , которой присваивает среднеезначение этих переменных. Затем значение, содержащееся в переменной d A v e r a g e ,возвращается вызывающей функции.Программисты иногда говорят, что "функция возвращает d A v e r a g e " . Это некорректное сокращение. Говорить, что передается или возвращается dAvera g e или иная переменная — неточно. В данном случае вызывающей функциивозвращается значение, содержащееся в переменной d A v e r a g e .Вызов A v e r a g e () из функции T e s t () выглядит точно так же, как и вызов любойдругой функции; однако значение типа d o u b l e , возвращаемое функцией A v e r a g e d ,сохраняется в переменной d A v e r a g e V a l u e .162Часть III.
Объектно-основанное программирование]Функция, которая возвращает значение (как, например, A v e r a g e ( ) ) , не можетзавершиться просто по достижении закрывающей фигурной скобки, посколькуС# совершенно непонятно, какое же именно значение должна будет вернуть этафункция? Для этого обязательно наличие оператора r e t u r n .Возврат значения посредством передачи по ссылкеФункция может также вернуть одно или несколько значений вызывающей программес помощью ключевых слов r e f или o u t . Рассмотрим пример U p d a t e () из раздела"Передача по ссылке" данной главы:// U p d a t e - функция пытается модифицировать значения//аргументов, переданные ей; обратите внимание, на// передачу аргументов как r e f и o u tpublic static void Update (ref int i, out double d){i = 10;d = 20.0;}Эта функция объявлена как v o i d , так как она не возвращает никакого значения вызывающей функции.
Однако поскольку переменная i объявлена как r e f , а переменнаяd— как o u t , любые изменения значений этих переменных, выполненные в функцииUpdate ( ) , сохранятся при возврате в вызывающую функцию. Другими словами, значения этих переменных вернутся вызывающей функции.Когда какой метод использоватьВы можете задуматься: "Функция может возвращать значение как с использованиемоператора r e t u r n , так и посредством переменных, переданных по ссылке. Так какой жеметод мне лучше применять в своих программах?" В конце концов, ту же функцию Average () вы могли написать и так:public c l a s s{Example// Примечание: параметр, передаваемый как ' o u t ' ,// сделать последним в спискеpublic s t a t i c v o i d A v e r a g e ( d o u b l e 1 , d o u b l e d 2 ,out double dResults)лучше{dResults = (dl + d2) / 2;}publicstaticvoidTest(){double vl = 1.0;d o u b l e v2 = 3 .
0 ;doubledAverageValue;Average(dAverageValue,vl,v2);C o n s o l e . W r i t e L i n e ( " С р е д н е е " + vl+ " и " + v2 + " равно "+ dAverageValue);}}Глава 7. Функции функций163ОбычнозначениевызывающейфункциивозвращаетсяспомощьюоператЛr e t u r n , а не посредством o u t - а р г у м е н т а , хотя обосновать преимущество такого по;хода очень трудно.Возврат значения посредством передачи аргумента по ссылке иногда требу!дополнительных действий, которые будут описаны в главе 14, "Интерфейсы!структуры".
Однако обычно эффективность — не главный фактор при прин|тии решения о способе возврата значения из функции.•^S^Как правило, "метод o u t " используется, если требуется вернуть из функции несшико значений — например, как в следующей функции:publicclassExample{publicstaticvoid AverageAndProduct(double dl, double d2,out double dAverage,out double dProduct){}}dAveragedProduct==( d l + d2)dl * d 2 ;/ 2 ;Возврат из функции нескольких значений встречается не так часто, как мож»показаться. Функция, которая возвращает несколько значений, обычно дела»это путем возврата одного объекта класса, который инкапсулирует несколы;значений, или путем возврата массива значений. Оба способа приводят к бол*ясному и понятному исходному тексту.Нулевая ссылка и ссылка на нольСсылочные переменные, в отличие от переменных типов-значений, при создании инициализируются значением по умолчанию n u l l .
Однако нулевая ссылка (т.е. ссылка,инициализированная значением n u l l ) — это не то же, что ссылка на ноль. Например,две следующие ссылки совершенно различны:class{ExampleintnValue,-}// Создание нулевой ссылки r e f lExamplerefl;// Создание ссылки на нулевой объектE x a m p l e r e f 2 = new E x a m p l e О;r e f 2 .
n V a l u e = 0;Переменная r e f l пуста, как мой бумажник. Она указывает в "никуда", т.е. не указывает ни на какой реальный объект. Ссылка же r e f 2 указывает на вполне конкретныйобъект, значение которого равно нулю.Возможно, эта разница станет понятнее после следующего примера:stringstring164si;s2 ="";Часть III. Объектно-основанное программированаfjПо сути, возникает аналогичная с и т у а ц и я — si указывает на нулевой объект, в товремя как s2 — на пустую строку (на сленге программистов пустая строка иногданазывается нулевой строкой). Это очень существенное отличие, как становится ясноиз следующего исходного текста:// T e s t namespaceтестоваяTestпрограмма(usingSystem;publicclassProgram{publics t a t i cvoidMain(string []strings){Console.WriteLine("Эта программа исследует " +"функцию T e s t S t r i n g ( ) " ) ;Console.WriteLine();Example e x a m p l e O b j e c t = new E x a m p l e ( ) ;Console.WriteLine("Передачанулевогообъекта:");string s = null;exampleObject.TestString(s);Console.WriteLine();// Теперь п е р е д а е м в функцию нулевую(пустую)строкуConsole.WriteLine("Передача пустой с т р о к и : " ) ;exampleObject.TestString("");Console.WriteLine() ;// Наконец,передаем реальную строкуConsole.WriteLine("Передача реальной строки:");exampleObject.TestString("teststring");Console.WriteLine();// Ожидаем п о д т в е р ж д е н и я п о л ь з о в а т е л яConsole.WriteLine("Нажмите <Enter> для " +" з а в е р ш е н и я п р о г р а м м ы .