Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 37
Текст из файла (страница 37)
Поэтому если метод с одним параметром принимает объект типа А, а ему передается объект типа Н, унаследованный от А, то в отсутствии версии этого метода, которая бы принимала тип В, компилятор неявно преобразует переданный экземпляр к ссылке типа А, чтобы удовлетворить такой вызов. В зависимости от ситуации и величины набора перегруженных методов процесс выбора может оказаться довольно сложным. Обнаружилось, что лучше минимизировать количество неоднозначных перегрузок, при которых для удовлетворительного решения компилятору нужно выполнять неявные преобразования. Слишком много неявных преобразований могут затруднить понимание кода, вызывая необходимость применения отладчика, чтобы разобраться, что же именно происходит.
Это усложняет задачу персонала сопровождения. которому придется следовать за вами и разбираться во всех действиях. Это не значит, что неявные преобразования — плохая вещь при разрешении перегрузки, но лучше использовать их благоразумно и сдержанно, чтобы минимизировать неприятные сюрпризы в будущем.
Начиная с версии С№ 4.0, разрешение перегрузки может генерировать исключения во время выполнения при использовании нового типа г1упав1с. Более подробно тип с1упзи1с рассматривается в главе 17. Необязательные аргументы Проектировщики С№ всегда учитывали список запросов пользователей при разработке новой версии языка. С годами необязательные аргументы методов переместились на вершину этого списка. Но до появления С№ 4.0 не было достаточно веских причин для их добавления к языку.
В духе повышенной возможности взаимодействия в С№ 4.0 появилась концепция необязательных и именованных аргументов (о которых будет сказано в следующем разделе), а также тип буоая1с 1рассматривается в главе 17). Эти три средства значительно расширили возможности взаимодействия между С№ и другими технологиями, такими как СОМ, приблизив язык С№ по уровню взаимодействия к Аг1эца) Ваэ1с. Необязательные аргументы в С№ работают подобно своим аналогам в С++.
В объявлении метода для параметра можно указать значение по умолчанию. Любой параметр, не имеющий значение по умолчанию, рассматривается как обязательный. Из этого следует, что все параметры со значениями по умолчанию должны размещаться в конце списка параметров метода. Рассмотрим следующий пример; Классы, структуры и объекты 137 пятпд Яувгевт с1аяв ТеавиевЬег ( рпЬ1гс ТеавыевЪег( вггапя вггспя яггспс Ьоо1 ТеввМевЬег Рп11каве = Тп11кавет ТТС1е = СгС1е; Теав Сеапц 1вгп11Тсве = Твгп11ТТвет Мяпачег = вяпасег; гп11кяве, СгС1е = Уыпгпоип", сеав = "Япкпсип", Твгп11Тгве = Та1ве, вапаяег = пп11 ) ( ) рпЬ1гс ясгспд Рп113аве ( адепт ргачаге веСт рпЫгс згг1пд ТгС1е ( дег; рггчвге вегт ) рпЬ1гс вггспч Теав ( дег; ргачаге веС; ) рпЫТс Ьос1 1вгп11таве( яес; ргтчвсе вест ) рпЬ1гс ТеавневЬег нападет ( дег~ рггчаге вегт ) згвгас с1авв Епггурсспг ( вгаггс чо1б Ма1п() ( ТеавиевЬег Св = пеи ТеавневЬег( "М11Ссп Хаббавв" )) ) ) еггог СЯ173б: Яегзп1С рагавегег чв1пе гог 'Ссгте' впвг Ье я соврг1е-Саве сопягапС овибкя СЯ1736т Значение параметра по умолчанию для СТС1е дслкно быть константой, сущестзуввей зо время компиляции Именованные аргументы Именованные аргументы — новое средство, которое появилось в СЗ 4.0.
В действительности оно дополняет необязательные аргументы. Рассмотрим пример класса ТеавМевЬег, приведенный в прелы)тущем разделе. Предположим, что необходимо создать экземпляр ТеавневЬег, приняв все аргументы конструктора по умолчанию за исвпочением 1вГп11Т1ве. Для этого можно использовать именованные аргументы, как показано ниже: петля Яувгепц с1аяя ТеввМевЬег ( рпЫгс ТеавМевЬег( яггспд вггтпд яггтпд Ьоо1 ТеавмевЬег гп11наве, СгС1е = Узп)тпоип", Сеяв = Убпхпоип", Твгп11Т1ве = Тя1яе, вяпяяег = пп11 ) ( В этом примере в объявлении конструктора используются необязательные параметры, и, как видно в методе маап, при инициализации экземпляра ТеавневЬег участвуют ассоциированные необязательные аргументы.
Обратите внимание, что все значения параметров по умолчанию являются константами. В качестве значений параметров по умолчанию ничего кроме констант, существующих во время компиляции, указывать не разрешено. В случае нарушения этого правила компилятор выдаст следующее сообщение об ошибке: ! 38 Глава 4 Рс11ваве = Гп11яаае) ТгЫе = Ггп1е; Теав = Геапп 1вуп11Т1ае = Тврс11ТТае; Мападег = тападегп ) рпЫ1с впггпд Рп11иаае ( деп; рггчапе веп; ) рпЫ1с впггпд ТТГ1е ( деп) рг1чапе веп; ) рпЬ11с впг1пд Теав ( деп; ргтчапе веп; ) рсЫТс Ьоо1 1вгп11ТТае( деп; ргтчапе веп) рпЫ1с ТеавМевЬег Мападег ( деп) ргтчапе веп) ) ) впасгс с1авв ЯпсгуРоапп ( впаптс чотс Мвтп() ( Теапиеэпег Ге = пеэ ТеавиевЬег( "Ревет 01ЪЪопв", Твгс11Типе: Ггпе ) ) ) Обратите внимание на способ передачи аргумента Тзрп11ТТае конструктору. Применять именованные аргументы легко. Необходимо просто указать имя аргумента.
за которым следует двоеточие и значение, которое должно быть передано для этого аргумента. Остальные аргументы при вызове конструктора сохраняют свои значения по умолчанию. В приведенном выше вызове конструктора именованные аргументы фактически можно было бы использовать и для передачи значений обязательных аргументов. При этом можно было бы изменить порядок аргументов в списке вызова: впап1с чоьп Маап() ( Теапиевпег Гв = пеэ ТеаэиевЬег( Твгп11Тппе: Ггое, Гп11вате: "Ревет 61ЬЬопв" ) Именованные аргументы оказывают незначительное влияние на перегрузку методов. Позиционный список аргументов конструируется за счет комбинирования заданных позиционных аргументов вместе с именованными аргументами и всеми применимыми аргументами по умолчанию.
Как только позиционный список аргументов сконструирован, разрешение перегрузок выполняется нормально. Единственное место, где именованные аргументы представляют сложность — это виртуальные переопределения. рассмотрим следующий надуманный пример: ив)пд Яувпепц с1авв й роЫТс ч1гппа1 чогб ЬЬЯовеГЫпд( гпп х, Тпп у ) ( Сопво1е.кг( Ге) 5 пе ( "( О ), (1) ", х, у ) ) ) ) с1авв В: й ( рпЫгс очегггбе чо1с' ЬоЯоаеГЫпд( Тпп у, Тпп х ) ( Ьаве.РоЯовеппапд( у, х )и ) Классы, структуры н объекты 139 зкзкас с1азз ЕпкгуРоъпк ( зкак1с чо10 Маап () ( В Ь = пен В() т Ь,РоЯовекъапд( х: 1. У: 2 ): А з = Ьт з.соэовекпапд( х: 1, у: 2 ); Это совершенно экстремальный пример.
и поступать подобным образом в реальной разработке ни в коем случае нельзя! Эапуск на выполнение приведенного выше кода дает следующий вывод: 2, 1 1, 2 При переопределении виртуального метода в типе-наследнике должно выполняться единственное требование — соответствие типов параметров и их позиций объявлению базового метода. Как было показано выше, действительные имена аргументов могут измениться. Проблема в том.
что компилятор должен иметь надежную методику отображения именованного аргумента на позиционный аргумент виртуального метода. Он делает это, исполъзуя объявление метода статического типа переменной-ссылки. В приведенном выше примере две ссылки а и Ь ссылаются на один и тот же экземплар В.
Однако при вызове РояовесЬТпс с именованными аргументами имеет значение объявление Рояовесь1пд, ассоциированное со статическим типом соответствующей ссылки. В первом вызове Рояовесптпд через переменную ь, поскольку ее статическим типом является В, именованные аргументы разрешаются на основе определения В. Розовекптпс. Но в следующем вызове РозовеСЬ1пс через переменную а, из-за того, что ее статическим типом является А.
именованные аргументы разрешаются через обращение к определению А. РозовеСЬ1пд. Как видите, этого следует избегать любой ценой. поскольку оно определенно вносит некоторую излишнюю путаницу в код. И, наконец, следует учесть еще один момент: когда в списке аргументов присутствуют выражения, они вычисляются в том порядке, в котором появляются в списке, даже если этот список отличается от порядка позиционных параметров вызываемого метода.
Взгляните на следующий пример: пэтпо Яузтевт с1аэз А ( риЬ11с гоаб РоЯовеккапч( Тпк х, тпк у ) Сопэо1е.иг)сеъапе( "(О), (1)", х, у ) ) зтакас с1азз Ептгурогпт ( зкзсас ноас( Маап() ( А з = пен А()! а.Розовеквапд( сепегзтеуа1ие1(), Сепегзсеуа1ие2() // Теперь используем именованные аргументы а.оозовеккупд( у ." Яепегзкеуа1пэ2(), х: Бепегасеуз1пе1() )т ) эгзтъс Тпс Яепегзкеуз1пе1 О ( Сопэо1е.ыгъсеьъпе( "Вызов СепегзсеЧз1пе1" ) гетзгп 1; 140 Глава л зсасгс зпс Вепегэсеуа1ие2 О ( Сопзо1е.иг1сеь1пе( "Вызов Вепегзсеуа1ие2" гесигп 2г ) ) В результате запуска на выполнение приведенного выше кода получается следующий вывод: Вызов сепегэгеуа1пе1 Вызов сепегасеуа1ие2 1, 2 Вызов 6епегзсеуа1ие2 Вызов Йепегасеуа1ие1 1, 2 Обратите внимание, что порядок вызова методов Бепе гасеуа1ие1 и Бепегагеуа1се2 зависит от порядка, в котором они появляются в списке аргументов, независимо от того, с какими позиционными параметрами они ассоциированы.











