Глава.5 .Примитивные,.ссылочные.и.значимые.типы
тате производительность приложения падает. Если вы пишете приложение на
«чистом» языке C#, неприятная ситуация может подстерегать вас только во время
работы с информацией, определяемой на этапе выполнения, когда вы используете
отражения (см. главу 23). Однако многие разработчики используют также С# для
связи с компонентами, не реализованными на С#. Некоторые из этих компонентов
могут быть написаны на динамических языках, например Python или Ruby, или
быть COM-объектами, которые поддерживают интерфейс
IDispatch
(возможно,
реализованный на С или C++), или объектами модели DOM (Document Object
Model), реализованными при помощи разных языков и технологий. Взаимодействие
с DOM-объектами особенно полезно для построения Silverlight-приложений.
Для того чтобы облегчить разработку при помощи отражений или коммуни-
каций с другими компонентами, компилятор С# предлагает помечать типы как
динамические
(
dynamic
). Вы также можете записывать результаты вычисления вы-
ражений в переменную и пометить ее тип как динамический. Затем динамическое
выражение (переменная) может быть использовано для вызовов членов класса,
например поля, свойства/индексатора, метода, делегата, или унарных/бинарных
операторов. Когда ваш код вызывает член класса при помощи динамического выра-
жения (переменной), компилятор создает специальный IL-код, который описывает
желаемую операцию. Этот код называется
полезной нагрузкой
(payload). Во время
выполнения программы он определяет существующую операцию для выполнения
на основе действительного типа объекта, на который ссылается динамическое вы-
ражение (переменная).
Следующий код поясняет, о чем идет речь:
internal static class DynamicDemo {
public static void Main() {
dynamic value;
for (Int32 demo = 0; demo < 2; demo++) {
value = (demo == 0) ? (dynamic) 5 : (dynamic) "A";
value = value + value;
M(value);
}
}
private static void M(Int32 n) { Console.WriteLine("M(Int32): " + n); }
private static void M(String s) { Console.WriteLine("M(String): " + s); }
}
После выполнения метода
Main
получается следующий результат:
M(Int32): 10
M(String): AA
Для того чтобы понять, что здесь происходит, обратимся к оператору
+
. У этого
оператора имеются операнды типа с пометкой
dynamic
. По этой причине компи-
лятор С# генерирует код полезной нагрузки, который проверяет действительный
тип переменной
value
во время выполнения и определяет, что должен делать
оператор
+
.
179
Примитивный.тип.данных.dynamic
Во время первого вызова оператора
+
значение его аргумента равно 5 (тип
Int32
),
поэтому результат равен 10 (тоже тип
Int32
). Результат присваивается переменной
value
. Затем вызывается метод
M
, которому передается
value
. Для вызова метода
M
компилятор создает код полезной нагрузки, который на этапе выполнения будет
проверять действительный тип значения переменной, переданной методу
M
. Когда
value
содержит тип
Int32
, вызывается перегрузка метода
M
, получающая параметр
Int32
.
Во время второго вызова
+
значение его аргумента равно
A
(тип
String
), а ре-
зультат представляет собой строку
AA
(результат конкатенации
А
с собой). Затем
снова вызывается метод
M
, которому передается
value
. На этот раз код полезной
нагрузки определяет, что действительный тип, переданный в
M
, является строковым,
и вызывает перегруженную версию
M
со строковым параметром.
Когда тип поля, параметр метода, возвращаемый тип метода или локальная
переменная снабжаются пометкой
dynamic
, компилятор конвертирует этот тип
в тип
System.Object
и применяет экземпляр
System.Runtime.CompilerServices.
DynamicAttribute
к полю, параметру или возвращаемому типу в метаданных. Если
локальная переменная определена как динамическая, то тип переменной также
будет типом
Object
, но атрибут
DynamicAttribute
неприменим к локальным пере-
менным из-за того, что они используются только внутри метода. Из-за того, что
типы
dynamic
и
Object
одинаковы, вы не сможете создавать методы с сигнатурами,
отличающимися только типами
dynamic
и
Object
.
Тип
dynamic
можно использовать для определения аргументов типов обоб-
щенных классов (ссылочный тип), структур (значимый тип), интерфейсов, деле-
гатов или методов. Когда вы это делаете, компилятор конвертирует тип
dynamic
в
Object
и применяет
DynamicAttribute
к различным частям метаданных, где это
необходимо. Обратите внимание, что обобщенный код, который вы используете,
уже скомпилирован в соответствии с типом
Object
, и динамическая отправка не
осуществляется, поскольку компилятор не производит код полезной нагрузки
в обобщенном коде.
Любое выражение может быть явно приведено к
dynamic
, поскольку все вы-
ражения дают в результате тип, производный от
Object
1
. В общем случае компи-
лятор не позволит вам написать код с неявным приведением выражения от типа
Object
к другому типу, вы должны использовать явное приведение типов. Однако
компилятор разрешит выполнить приведение типа
dynamic
к другому типу с ис-
пользованием синтаксиса неявного приведения.
Object o1 = 123; // OK: Неявное приведение Int32 к Object (упаковка)
Int32 n1 = o1; // Ошибка: Нет неявного приведения Object к Int32
Int32 n2 = (Int32) o1; // OK: Явное приведение Object к Int32 (распаковка)
dynamic d1 = 123; // OK: Неявное приведение Int32 к dynamic (упаковка)
Int32 n3 = d; // OK: Неявное приведение dynamic к Int32 (распаковка)
1
И как обычно, значимый тип будет упакован.
180
Do'stlaringiz bilan baham: |