ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 11.12.2025

Просмотров: 3224

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

/ /

P a s s B y R e f e r e n c e -

/ /

п е р е д а ч и а р г у м е н т а

u s i n g S y s t e m ;

п р о г р а м м а д л я д е м о н с т р а ц и и с е м а н т и к и п о с с ы л к е

n a m e s p a c e

P a s s B y V a l u e

{

 

p u b l i c

c l a s s P r o g r a m

{

/ / U p d a t e - ф у н к ц и я п ы т а е т с я м о д и ф и ц и р о в а т ь з н а ч е н и я / / а р г у м е н т о в , п е р е д а н н ы е е й ; о б р а т и т е в н и м а н и е , н а / / п е р е д а ч у а р г у м е н т о в к а к r e f и o u t

p u b l i c

s t a t i c v o i d U p d a t e ( r e f i n t

i , o u t d o u b l e d )

{

 

 

 

i

=

1 0 ;

 

d

=

2 0 . 0 ;

 

}

 

 

 

p u b l i c

s t a t i c v o i d M a i n ( s t r i n g [ ]

a r g s )

{

/ / О б ъ я в л я е м д в е п е р е м е н н ы е и о д н у и н и ц и а л и з и р у е м

i n t i =

1 ;

d o u b l e

d ;

C o n s o l e . W r i t e L i n e ( " П е р е д в ы з о в о м " +

 

" U p d a t e ( r e f i n t , o u t d o u b l e ) : " ) ;

C o n s o l e . W r i t e L i n e ( " i = " + i +

 

" , d н е и н и ц и а л и з и р о в а н а " ) ;

/ / В ы з ы в а е м ф у н к ц и ю

' U p d a t e ( r e f i , o u t d ) ;

/ / О б р а т и т е в н и м а н и е — з н а ч е н и е i р а в н о 1 0 , з н а ч е н и е d / / р а в н о 2 0 . 0

C o n s o l e . W r i t e L i n e ( " П о с л е в ы з о в а "

" U p d a t e ( r e f i n t , o u t d o u b l e ) : " ) , - C o n s o l e . W r i t e L i n e ( " i = " + i + ", d = " + d ) ;

/ / О ж и д а е м п о д т в е р ж д е н и я п о л ь з о в а т е л я

C o n s o l e . W r i t e L i n e ( " Н а ж м и т е < E n t e r > д л я " +

" з а в е р ш е н и я п р о г р а м м ы . . . " ) ;

C o n s o l e . R e a d ( ) ;

Ключевое слово r e f указывает С#, что в функцию следует передать ссылку на i, а не просто значение этой переменной. Следовательно, изменения, выполненные с этой переменной, оказываются экспортированы обратно вызывающей программе.

Подобно этому, ключевое слово o u t говорит: "Передай ссылку на эту переменную, но можешь никак не заботиться о ее значении, поскольку оно все равно не будет исполь­ зоваться и будет перезаписано в процессе работы функции". Это ключевое слово годится тогда, когда переменная применяется исключительно для того, чтобы вернуть значение вызывающей программе.

158

Часть III. Объектно-основанное программирование


Выполнение рассмотренной программы P a s s B y R e f е г е п с е приводит к следующе­

му выводу на экран:

 

 

 

 

 

Перед

вызовом

U p d a t e ( r e f

i n t ,

 

o u t d o u b l e )

:

i = 1, d н е и н и ц и а л и з и р о в а н а

 

 

 

После

вызова

U p d a t e

( r e f

i n t ,

o u t d o u b l e )

:

i = 10,

d = 20

 

 

 

 

 

Нажмите

< E n t e r > д л я

з а в е р ш е н и я

п р о г р а м м ы . . .

 

Аргумент, передаваемый как o u t , всегда считается передаваемым так же как

 

ref, т.е. писать

r e f o u t — это

тавтология. Кроме того, при передаче аргу­

 

ментов по ссылке вы должны

всегда передавать только переменные — переда­

 

ча литеральных значений, например просто числа 2 вместо переменной типа

 

i n t , в этом случае приводит к ошибке.

 

Обратите внимание, что начальные значения i и d переписываются в функции Up ­ dated . После возврата в функцию M a i n () эти переменные остаются с измененными в функции U p d a t e () значениями. Сравните это поведение с программой P a s s B y ­ Value, где внесенные изменения не сохраняются при выходе из функции.

Не передавайте переменную по ссылке дважды

Никогда не передавайте по ссылке одну и ту же переменную дважды в одну функцию, поскольку это может привести к неожиданным побочным эффек­ там. Описать эту ситуацию труднее, чем просто продемонстрировать при­ мер программы. Внимательно взгляните на функцию U p d a t e () в приве­ денном листинге.

//

PassByRef e r e n c e E r r o r

- д е м о н с т р а ц и я

п о т е н ц и а л ь н о

/ /

ошибочной

с и т у а ц и и

п р и в ы з о в е ф у н к ц и и с

п е р е д а ч е й

// а р г у м е н т о в по с с ы л к е

 

 

 

using S y s t e m ;

 

 

 

 

 

 

namespace

P a s s B y R e f e r e n c e E r r o r

 

 

 

{

 

 

 

 

 

 

 

 

p u b l i c c l a s s

P r o g r a m

 

 

 

 

{

 

 

 

 

 

 

 

 

 

/ / U p d a t e - э т а ф у н к ц и я п ы т а е т с я и з м е н и т ь з н а ч е н и я

 

/ / п е р е д а н н ы х е й а р г у м е н т о в

 

 

 

 

p u b l i c

s t a t i c

v o i d

D i s p l a y A n d U p d a t e ( r e f

i n t

n V a r l ,

 

 

 

 

 

 

r e f

i n t

n V a r 2 )

 

{

 

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " Н а ч а л ь н о е з н а ч е н и е n V a r l - " +

 

 

 

 

 

n V a r l ) ;

 

 

 

 

n V a r l

=

1 0 ;

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " Н а ч а л ь н о е з н а ч е н и е n V a r 2 - " +

 

 

 

 

 

n V a r 2 ) ;

 

 

 

 

n V a r 2

=

2 0;

 

 

 

 

 

 

}

 

 

 

 

 

 

 

 

p u b l i c

s t a t i c

v o i d

M a i n ( s t r i n g [ ]

a r g s )

 

 

 

{

 

 

 

 

 

 

 

Глава 7. Функции функций

159



/ / О б ъ я в л я е м и и н и ц и а л и з и р у е м п е р е м е н н у ю

i n t

п = 1 ;

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " П е р е д в ы з о в о м " +

 

 

 

" U p d a t e ( r e f

n ,

r e f

n ) : " ) ;

C o n s o l e . W r i t e L i n e ( " n = " + n ) ;

 

 

C o n s o l e . W r i t e L i n e ( ) ;

 

 

 

 

/ / В ы з ы в а е м ф у н к ц и ю

 

 

 

 

D i s p l a y A n d U p d a t e ( r e f n ,

r e f

n ) ;

 

 

/ / О б р а т и т е в н и м а н и е н а т о ,

к а к и з м е н я е т с я з н а ч е н и е п

/ / - н е т а к ,

к а к о ж и д а л о с ь о т э т о й п е р е м е н н о й и

/ / ф у н к ц и и ,

в к о т о р у ю о н а б ы л а п е р е д а н а

C o n s o l e . W r i t e L i n e ( ) ;

 

 

 

 

C o n s o l e . W r i t e L i n e ( " П о с л е в ы з о в а "

+

 

 

 

" U p d a t e ( r e f

n ,

r e f

n ) : " ) ;

C o n s o l e . W r i t e L i n e ( " n = " + n) ;

 

 

/ /

О ж и д а е м п о д т в е р ж д е н и я

п о л ь з о в а т е л я

 

C o n s o l e . W r i t e L i n e ( " Н а ж м и т е < E n t e r > д л я " +

 

 

" з а в е р ш е н и я п р о г р а м м ы . . . " ) ;

C o n s o l e . R e a d ( ) ;

 

 

 

 

}

 

 

 

 

 

 

}

 

 

 

 

 

 

В этом примере функция U p d a t e ( r e f

i n t , r e f

i n t )

объявлена как функцию

с двумя целыми аргументами, передаваемыми по ссылке — ив этом нет ничего некор ректного или необычного. Проблема возникает тогда, когда в функцию передается одна

ита же переменная как в качестве первого, так и второго аргумента. Внутри функции происходит изменение переменной n V a r l , которая ссылается на переменную п, от ее

начального значения 1 до значения 10. Затем происходит изменение переменной nVar которая тоже ссылается на переменную п, от ее начального значения 1 до значения 20:

ипобочное действие заключается в том, что переменная п теряет ранее присвоенное значение 10 и получает новое значение 20.

Это видно из приведенного ниже вывода программы на экран: П е р е д в ы з о в о м U p d a t e ( r e f n , r e f n ) :

n = 1

Н а ч а л ь н о е з н а ч е н и е n V a r l

-

1

Н а ч а л ь н о е з н а ч е н и е n V a r 2

-

1 0

П о с л е в ы з о в а U p d a t e ( r e f n ,

r e f n ) :

n = 2 0

 

 

Н а ж м и т е < E n t e r > д л я з а в е р ш е н и я п р о г р а м м ы . . .

Понятно, что ни программист, который писал функцию U p d a t e ( ) , ни про граммист, ее использовавший, не рассчитывали на такой экзотический результат. Вся проблема оказалась в том, что одна и та же переменная была передан в одну и ту же функцию по ссылке больше одного раза. Никогда так не посту пайте, если только вы не абсолютно уверены в том, чего именно вы хотите до биться таким действием.

160

Часть III. Объектно-основанное программированы


Вы можете передавать одно и то же значение в функцию сколько угодно раз, если передаете его по значению.

Почему некоторые аргументы используются только для возврата значений?

С# по мере возможности старается защитить программиста от всех глупостей, которые он может вольно или невольно сделать. Одна из этих глупостей — программист может забыть проинициализировать переменную перед ее первым применением (особенно час­ то это случается с переменными-счетчиками). С# генерирует ошибку, когда вы пытае­ тесь использовать объявленную, но не инициализированную переменную:

i n t n V a r i a b l e ;

 

 

C o n s o l e . W r i t e L i n e ( " Э т о о ш и б к а

"

+ , n V a r i a b l e ) ;

n V a r i a b l e = 1;

 

 

C o n s o l e . W r i t e L i n e ( " А э т о - н е т

"

+ n V a r i a b l e ) ;

Однако C# не в состоянии отслеживать переменные в функции:

void S o m e F u n c t i o n ( r e f i n t n V a r i a b l e )

{

C o n s o l e . W r i t e L i n e ( " О ш и б к а и л и н е т ? " + n V a r i a b l e ) ;

} ,

Откуда функция S o m e F u n c t i o n () может знать, была ли переменная n V a r i a b l e инициализирована перед вызовом функции? Это невозможно. Вместо этого С# отсле­ живает переменные при вызове функции — например, вот такой вызов функции при­ ведет к сообщению об ошибке:

i n t n U n i 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 ( r e f 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 () полу­ чила бы ссылку на неинициализированную переменную (т.е. на мусор (garbage)) в па­ мяти. Ключевое слово o u t позволяет функции и вызывающему ее коду договориться о том, что передаваемая по ссылке переменная может быть не инициализирована — функция обещает не использовать ее значение до тех пор, пока оно не будет какимлибо образом присвоено самой функцией. Следующий фрагмент исходного текста компилируется без каких-либо замечаний:

i n t n U n i 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 ( o u t n U n i n i t i a l i z e d V a r i a b l e ) ;

Передача инициализированной переменной как out-аргумента также является кор­ ректным действием:

i n t n l n i t i a l i z e d V a r i a b l e = 1 ;

S o m e F u n c t i o n ( o u t 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