Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 36
Текст из файла (страница 36)
Поскольку в С№ нет нотации константных параметров, для передачи константных параметров должны создаваться неизменяемые ((шшц(аЫе) объекты. Более подробно зти объекты рассматриваются в главе 13. На заметку! Разработчики на С++, которые привыкли применять идиомы дескриптор/тело (Ьзпб(е/ Ьобу) для реализации семантики копирования при записи, должны принимать этот факт эо внимание.
Это не значит, что применять эти идиомы з С№ невозможно; скорее, это просто означает, что они должны быть реализованы иначе. Аргументы-значения В действительности все параметры, передаваемые в методы, являются аргументами-значениями, если предполагается, что они — нормальные, простые, недекорированные параметры методов. Под недекорированными понимается отсутствие специальных Классы, структуры и объекты 133 ключевых слов, таких как опт, гег н рагзвя, которые могут быть присоединены к ним. Однако они могут иметь присоединенные атрибуты, как почти все что угодно в системе типов С).К.
Подобно всем параметрам, идентификатор находится в контексте блока метода, который следует за списком параметров (те. внутри фигурных скобок), а метод получает копию переданной переменной в момент его вызова. Однако будьте осторожны с тем, что это означает.
Если переданная переменная является структурой, или типом значения, то метод получает копию этого значения. Любые изменения, проведенные локально в этой переменной, вызывающему коду не видны. Если же передаваемая переменная является ссылкой на объект в куче, как любая переменная для экземпляра класса, то метод получает копию ссылки. То есть любые изменения. проведенные в объекте через такую ссылку, становятся видимыми коду, вызвавшему метод. Аргументы ген Передача параметров по ссылке отмечается помещением модификатора тес перед типом параметра в списке параметров метода. Когда переменная передается по ссылке, новая копия этой переменной не создается, и эту переменную из вызывающего метода напрямую затрагивают все действия внутри метода.
Как обычно бывает в СЬК, это означает две слегка отличающиеся вещи, в зависимости от того, является переменная экземпляром типа значения (структуры) или же объекта (класса). Когда экземпляр значения передается по ссылке, копия значения из вызывающего кода не создается. Это подобно передаче параметра как указателя в С++, несмотря на то, что обращение к методам и полям переменной производится точно так же, как к аргументам-значениям. Когда экземпляр объекта (ссылки) передается по ссылке. никакой копии переменной не создается. Фактически переменная ведет себя подобно указателю С++ на ссылочную переменную, что в С++ выглядит как указатель на указатель.
Вдобавок верификатор проверяет, чтобы переменная, на которую ссылается параметр гег, имела определенное присвоенное значение перед вызовом метода. Рассмотрим некоторые примеры применения полной нотации гег-параметров: пягпд Яуясевт РиЬ11с ятгясг Муаггпсг ( Рпы11с 1пг ча1т РпЬ1гс с1аяя ЯпггуРо1пс ( ясэгас чо1т( Мз1п () ( муясгпсг вуча1пе = пеи муяггпсс () т вуЧа1пе.ча1 = 10; РяяяВуЧа1пе ( вуЧа1пе Сопяо1е.ыгасеъзпе( "Результат РзяяВуЧа1пе: вуЧа1пе.ча1 = (О)", вуЧа1пе.ча1 )т Рэяявукег( гег вууз1пе )) Сопяо1е.ыггсеъгпе( "Результат Раяявукег: вуЧа1пе.ча1 = (О)", вуЧа1пе.чз1 )к ) ягатус чо1с( РаяяВуЧэ1пе ( Музсгпсг вуЧа1пе ) вуЧа1пе.ча1 = Яот якагус чо1т) Раяявукес( гег МуЯСгпсс вуЧа1пе ) ( вуча1ие.ча1 = 42) ) 134 Глава 4 В примере содержатся два метода: Раззвууа1пе и Раззвукеу.
Оба метода модифицируют поле экземпляра типа значения. переданного им. Однако, как доказывает приведеииый ниже вывод, метод Раззкууа1пе модифицирует локальную копию, в то время как метод Ра за Вукег модифицирует экземпляр из вызывающего кода: Результат РаззВуча1пе: иуча1се.ча1 = 10 Результат Разззуаеб: иууа1пе.ча1 = 42 Кроме того, обратите внимание,что в точке вызова метода РаззВукег требуется ключевое слово геб. Это необходимо, потому что метод может быть перегружен иа основе присутствия или отсутствия ключевого слова геб. Другими словами, другой метод РаззВуйег мог бы также принимать МуВсгпсС по значению.
а ие по ссылке. Плюс к этому обязательное применение ключевого слова гег в точке вызова облегчает чтение кода. Теперь рассмотрим пример, в котором вместо типа значения используется объект: П51ПЯ Вузгюав рсЬ11с с1азз ЕпггуРотпС ( згаС1с чотб Матс() ( оЬ7есс иуОЬ)есс = пен ОЬ)есс()) Сопзо1е.игтгеъьпе( ГвуОЬ7есС.ОеСНавЬСоое О иуОЬ)есС.ОеСНазЬСопе() ); Раззвукег( гег иуОЬ)ес ): Сопзо1е.кгггеьупе( ГвуОЬ7'есС.ОеСНазЬСос(е О иуОЬ7'есС.ОеСНазпсо4(е() (О) (О] ) зьагус чотс( РаззВуаег( геЕ оЬ7есС иуОЬ7есС ) ( // Присвоить новый экземпляр этой переменной иуОЬ7есс = пен ОЬ7есс(); ) ) иуОЬ7есС.ОеСНазЬСопе О == 46104728 иуОЬ7есС.СеСНазЬСоь(е() == 12289376 Параметры оп~ Параметры опс (выходиые) почти идентичны параметрам геб, ио с двумя существеииыми отличиями.
Во-первых. вместо ключевого слова геб используется ключевое слово опс, и оио также должно применяться в точке вызова, как зто было в случае геу. Во-вторых, переменная, иа которую ссылается переменная опс, ие обязана иметь присвоеииое значение перед вызовом метода. как в случае геб-параметра. Причина в том, что методу ие позволено каким-либо полезным способом использовать переменную до тех пор, пока ей ие будет присвоено значение.
Например, следующий код совершенно корректен: В этом случае переданная по ссылке переменная является объектом. Но как было сказано, вместо того, чтобы передать в метод копию ссылки, тем самым создавая иовую ссылку иа тот же объект, здесь передается сама исходная ссылка.
Да, звучит запутанно. В предыдущем методе Раз зкукеу переданная ссылка переустаиавливается иа новый экземпляр объекта. Исходный объект остается без ссылок, и потому готов к тому, чтобы его обработал сборщик мусора. Для получения доказательств, что новая перемеииая ауОЬ) есС ссылается иа два разных экземпляра в точке вызова и в точке. следующей за вызовом, результаты вызовов АСЬ) есС. ОегказЬСос1е отправляются иа консоль. Полученный в результате вывод показан ниже: Классы, структуры и объекты 135 рпЬ11с с1звв ЕпсгуРокпг ( ясаскс чотб Макп() ( оЬ3есс оЬ3; РаввАвоиГРягяш( оис оЬ3 ) ) ясвстс чотб РяяялвбчГРагаш( оис оЬ)еск оЬ3 ) ( оЬ3 = пеы ОЬ3есс()) Обратите внимание, что переменной оЬ3 в методе Ма 1п перед вызовом Рая я А я Оп с Рагзш значение напрямую ие присваивается.
И это правильно, потому что оиа помечена как оцг-параметр. Метод РаввАвОцграгаш ие обращается к этой переменной, пока ие присвоит ей значение. Если вы попробуете заменить два вхождения опт иа тес в приведениом выше коде, то получите ошибку компизяции, подобную следующей: еггог СБ0165: Ояе ос ппвввтдпеб 1оса1 чагквЬ1е 'оЬ3' ошибка СЯО165т Использование неинициализированной локальной переменной оЬУ Массивы разбаша С№ позволяет легко передавать переменный список параметров. Для этого просто объявите последний параметр в списке параметров с типом массива, предварив его ключевым словом рагашв.
Теперь, если метод вызывается с переменным количеством параметров, то эти параметры передаются ему в форме массива, по которому легко выполнить итерацию, и тип этого массива может базироваться иа любом допустимом типе. Ниже приведен краткий пример: пакли Яуягепц рцЬ11с с1аяя ЕпсгуРотпс ( вгас1с чотб Мвкп() ( нзглгсв( 42 )т НзгАгцз( 42, 43, 44 ): НагАгся( 44, 56, 23, 234, 45, 123 )т ) всвскс чотб Нзглгця( кпс чя11, рагашз 1пс[) ча1в ) ( Сопяо1е.вг1сеЫпе( "чя11: (О)", ча11 ); тогеасп( тпс 1 кп ча1в ) ( Сопво1е.игтсеътпе( "чз1в[): (О)", )т ) Сопво1е.вгасеьапе()т ) В каждом случае нагАгсв вызывается успешно, ио в каждом случае массив, переданный в чз1в, отличается. Как видите, передача переменного числа параметров в С№ замечательно проста.
Можете построить эффективный метод АтЫ для контейнерного типа, применяя массивы параметров, когда для добавления перемеииого количества злемеитов необходим только один вызов. 136 Глава 4 Перегрузка методов Перегрузка в С№ — прием времени компиляции, при котором в точке вызова компилятор выбирает метод из набора одноименных методов с разной сигнатурой. Для выбора наиболее подходящего метода компилятор использует список аргументов. Типы аргументов, а также модификаторы параметров геу, оис и рагатз играют роль в перегрузке методов, поскольку все они составляют часть сигнатуры метода.
Методы без параметров-массивов переменной длины получают преимущества перед методами, имеющими такие параметры. Подобно С++, тип возврата метода не является частью сигнатуры (за исключением одного редкого случая операций преобразования, о котором будет рассказано в главе 6). Поэтому внутри класса нельзя иметь перегруженные методы, отличающиеся только типом возврата. И, наконец, если компилятор доходит до точки, где обнаруживается неоднозначность в выборе версии перегруженного метода. компиляция останавливается с выдачей сообщения об ошибке. В целом между перегрузкой методов в С№ и перегрузкой методов в С++ разницы нет. Обычно она не может послужить причиной возникновения исключений времени выполнения, поскольку весь алгоритм выбора применяется во время компиляции. Когда компилятор не может найти точно подходящий метод на основе переданных параметров, он начинает подбор наиболее подходящего соответствия на основе возможности неявного преобразования экземпляров в списке параметров.












