Нэш Трей - C# 2010. Ускоренный курс для профессионалов (2010) (1160865), страница 152
Текст из файла (страница 152)
Поэтому компилятор упаковывает все, что известно во время компиляции, в место вызова, которое проделает остаток работы по разрешению перегрузки во время выполнения. Как и можно было догадаться, этот код сгенерирует исключение во время выполнения, поскольку с( содержит экземпляр О. а экземпляры р не являются неявно преобразуемыми ни в 1пг, ни в всг1пд.
В отношении разрешения перегрузки тип бупамьс поставил перед командой проектировщиков С№ ряд непростых проблем, и им пришлось находить непростые пути их решения. Некоторые из проблем рассматриваются в следующем разделе. Динамическое разрешение перегрузки Задача разрешения перегрузки была достаточно сложной и до появления типа Оупавьс.
В разных местах этой книги уже говорилось о разрешении перегрузки и о том, как она работает в контексте методов классов, определений интерфейсов и расширяющих методов. Динамические типы добавляют к проблеме разрешения перегрузки новое измерение. В предыдущем разделе упоминалось, что динамические переменные экземпляра неявно преобразуемы только в тип г(упвгв1с и ни в какой другой. Как должен по- Динамические типы 571 ступить компилятор, когда встречается вызов метода на статическом типе с передачей ему аргумента бупапасу Решение состоит в том, что всякий раз, когда тип бупзят1с участвует в вызове метода — если тип бупан1с имеет приемник или параметр.
— то разрешение перегрузки производится во время выполнения. Зто несложный процесс. Рассмотрим следующий код: цякпст Яуясетт с1аяя С зсзк1с рцЬ11с чоаб Роо( боцЬ1е бЬ1, бупяиьс буп ) ( Сопзо1е.нгакеЬапе ( "чоаб Роо ( боцЬ1е бЫ, бупяиьс буп )" ) ) якакас рцЫ1с чотб Роо( 1пк 1, зкгбпо якг ) ( Сопяо1е.кгаке11пе( "чоаб Роо( 1пс 1, зкгкпст ясг )" )т ) якясас с1аяя Рпггурогп ( якяк1с чогб Мяап() ( бупяибс б = "1'в бупяпкс"т бупаибс б1 = пен оЬ)есс(); С.гоа( 3.1415, с( ); С.Роо( 42, б ); С.Гоо( 42, с(1 )т /с' )) это не компилируется! с'с' С.Роо( 5яе11о!", б ); // перегрузка времени компиляции? перегрузка времени выполнения с'/ перегрузка времени выполнения чоаб Роо( боцЬ1е бЬ1, бупав1с буп ) чо1б Рос ( 1пк 1 якг1пе зсг ) чоаб Гоо( боцЫе с(Ь1, бупяиас буп ) Как видите, несмотря на то, что в последних двух вызовах С.
Гоо оба параметра неявно преобразуемы в первую перегрузку С . Гоо. компилятор не должен производить выбор во время компиляции. Дело в том, что тип экземпляра бупан1с может быть исследован во время выполнения, и на основе полученной информации может быть сделан более точный выбор. И, наконец, обратите внимание на последний вызов С. Гоо, который в коде закомментирован. В таком виде, как он записан, он не может компилироваться.
Если каждый вызов, который включает бупапп'. с, разрешается во время выполнения, почему это так? На самом деле компилятор рассматривает множество методов-кандидатов и сообщает, Первый вызов С. Гоо в точности соответствует сигнатуре первой перегрузки, даже несмотря на то, что в вызове присутствует параметр бупанас. Может показаться.
что компилятор должен автоматически выбрать первую перегрузку во время компиляции, но на самом деле это не так (в противном случае нарушилось правило разрешения перегрузки бупан1с, которое было только что приведено). Второй вызов откладывает выбор перегрузки до времени выполнения, невзнраи на то, что оба параметра неявно преобразуемы в первую перегрузку, те. тип апс неявно преобразуем в боцЬ1е, а тнп бупанас — в бупааьс. Хотя поведение второго вызова С. Гоо поначалу может показаться не интуитивно понятным, поскольку тнп Рпп неявно преобразуем в боцЬ1е, а тип бупанбс — в бупатас. взгляните на третий вызов С.
Гоо (может быть. он станет более осмысленным, если посмотреть на показанный ниже результат). Вывод этого кода выглядит следующим образом: 572 Глава )7 что найти подходяшую перегрузку во время выполнения не удалось. Поэтому хотя действительный вызов определяется во время выполнения, компилятор применяет максимум логики во время компиляции, чтобы избежать ситуации, когда во время выполнения гарантированно будет сгенерировано исключение. На заметку! Если вы читали ранние материалы, касающиеся динамического разрешения перегрузки, то наверняка знакомы с концепцией метода-фантома. Эту технику команда разработчиков компилятора использовала ранее для выполнения разрешения перегрузки на статических приемниках с параметрами бупашас.
Позднее от упоминания методов-фантомов отказались в пользу современной трактовки всех таких вызовов с применением динамического поиска. Вспомните, что тип бупашас внутренне реализован как ссылка на оЬЯ есг с присоединенным атрибутом. Таким образом, внутри типа не могут быть перегрузки, в которых одна позиция параметра отличается только типом бупашьс или оЬ1есг. Например, следующий код компилироваться не будет; с1авз С ( // Не поступайте так!)! рпь11с чоаб роиогк( оь9есг о ) ( ) рпЬ|зс чо16 Ооногх( бупаш1с б ) ( ) ) Попытка компиляции приводит к выдаче примерно такого сообщения об ошибке: Туре 'С' з1геабу бетапез з шешЬег сз11еб 'боногх' иьсп Гпе ваше рагзшесег курев Я типе С уяе определен член по имени ооног)с с теми ке самыми типами параметров Динамическое наследование Тип с)упатас привносит определенные сложности в отношении наследования.
Имеет ли смысл наследовать от с)упаш1с? Можно ли выполнять наследование от обобщенного базового типа, когда один из аргументов-типов является бупаш1с? Можно ли реализовать обобщенный интерфейс, у которого один из аргументов-типов является бупашас? Эти вопросы будут рассматриваться в настоящем разделе, но сначала давайте посмотрим, что нельзя делать с наследованием и типом с(упаш1с. Нельзя наследовать от йупалйс Одна из вещей, которые делать нельзя — это использовать с)упапп.с в качестве базового класса.
То есть следующий код недопустим: // Это не компилируется!!! с1ззз С: бупзш1с ( ) Попытка компиляции такого кода приводит к выдаче следующего сообщении об ошибке: еггог СЯ1965: 'С': сзппос бегьче Тгош Гпе бупзшас Гуре ошибка СЯ1965г Сг наследовать от типа бупашзс нельзя Учитывая тот факт, что в скомпилированном коде )Ь тип бупашас заменяется на оЬ1есс,может возникнуть вопрос, почему нельзя наследовать от бупашас? Но даже если бы это было возмоясно, что оно даст? Ровным счетом ничего! Динамические типы 573 Если бупавбс — на самом деле оЬ] ест, ту же реализацию Яуэгев. ОЬ] ест можно унаследовать, просто указав оЬ]ест в качестве базового типа для С.
И поскольку Яузгев. ОЬ] ест — базовый тип по умолчанию для всех типов, это означало бы просто лишний клавиатурный ввод при наборе кода. Более того, С является статическим типом. Поэтому даже если бы можно было наследовать от бупалп с, то полученный в результате тип все равно был бы статическим, что полностью исключает его динамическую природу.
Короче говоря, наследование от с(упав). с вообще не имеет смысла. Нельзя реализовывать интерфейсы двупало.с Стоит повторить еще раз: на уровне СЬЙ тип бупавбс — зто всего лишь оЬ]ест с присоединенными атрибутами. Имея это в виде, рассмотрим следующий код: // Зто не компилируется!!! псеггасе 1ног)с<Т> ( яо[б Понос)с ( Т [сев ) ( с1азз С: 1когн<бупавтс> ( рпЫфс тогб понос)с( бупав1с 1сев ) ( ) ) Попытка компиляции этого код приводит к выдаче следующего сообщения об ошибке: еггог Ся1966с 'С': саппог [вр1евепг а бупав1с 1пгеггасе '1когн<бупавфс>' овибка С$1966т Сс реализоаать динамический интерфейс ткоги<булав]с> нельзя Компилятор сообщает о том, что реализовать обобщенный интерфейс с указанием типа с(упав1с для обобщенного аргумента нельзя.
Почему? На то есть две веские причины. Первая — техническая. В настоящее время в среде СЬК [реализующей специфинацию СЬ]) нет способа применения атрибута ОупавбсАСГг1Ьпсе или любого другого атрибута к аргументу обобщения. Чтобы проиллюстрировать это, взглянем на следующий код, который также не компилируется: // Зто не компилируется!!! пзфпд Яузгевт с1азз Ярес1а1АГГгфЬпсе: Атег[Ьпге [псеггасе 1ногк<т> ( чофб Ооногн( Т усев ) с1азз С: 1ког)с< [Вресьа1] оЬ)есс > ( рпЫ[с уофб Помоги( (Яресха1) оЬ]есс 1тев ) ( ) ) Если декорировать обобщенный аргумент атрибутом, как это сделано выше, то компилятор выдаст сообщение об ошибке, которая означает, что компилятор не имеет понятия о том, что вы пытаетесь сделать, поскольку не сможет выполнить базовую верификацию синтаксиса: 574 Глава (7 депег1с аггггЬпсе.св(13,18): еггог СЯ1031: Туре ехрессеб (Омндается тип) цепег1с агггТЬпсе.ся(13,35): еггог ся1519: Тпча11с) го)сеп '>' гп с1аяв, всгпсс, ог 1псеггасе шешЬег с)ес1агас1оп (Неверная лексема '>' в объявлении члена класса, структурн или интерфейса) Вторая причина,по которой нельзя реализоватьдинамические интерфейсы,по своей природе практична.












