Рейтинговые книги
Читем онлайн Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 218 219 220 221 222 223 224 225 226 ... 407
Console.WriteLine("s2 is of type: {0}", s2.GetType());

  Console.WriteLine("s3 is of type: {0}", s3.GetType());

}

в случае вызова приведет к такому выводу:

s1 is of type: System.String

s2 is of type: System.String

s3 is of type: System.String

Динамическая переменная и переменная, объявленная неявно или через ссылку на System.Object, существенно отличаются тем, что динамическая переменная не является строго типизированной. Выражаясь по-другому, динамические данные не типизированы статически. Для компилятора C# ситуация выглядит так, что элементу данных, объявленному с ключевым словом dynamic, можно присваивать вообще любое начальное значение, и на протяжении периода его существования взамен начального значения может быть присвоено любое новое (возможно, не связанное) значение. Рассмотрим показанный ниже метод и результирующий вывод:

static void ChangeDynamicDataType()

{

  // Объявить одиночный динамический элемент данных по имени t.

  dynamic t = "Hello!";

  Console.WriteLine("t is of type: {0}", t.GetType());

  t = false;

  Console.WriteLine("t is of type: {0}", t.GetType());

  t = new List<int>();

  Console.WriteLine("t is of type: {0}", t.GetType());

}

Вот вывод:

t is of type: System.String

t is of type: System.Boolean

t is of type: System.Collections.Generic.List`1[System.Int32]

Имейте в виду, что приведенный выше код успешно скомпилировался и дал бы идентичный результат, если бы переменная t была объявлена с типом System.Object. Однако, как вскоре будет показано, ключевое слово dynamic предлагает много дополнительных возможностей.

Вызов членов на динамически объявленных данных

Учитывая то, что динамическая переменная способна принимать идентичность любого типа на лету (подобно переменной типа System.Object), у вас может возникнуть вопрос о способе обращения к членам такой переменной (свойствам, методам, индексаторам, событиям и т.п.). С точки зрения синтаксиса отличий нет. Нужно просто применить операцию точки к динамической переменной, указать открытый член и предоставить любые аргументы (если они требуются).

Но (и это очень важное "но") допустимость указываемых членов компилятор проверять не будет! Вспомните, что в отличие от переменной, определенной с типом System.Object, динамические данные не являются статически типизированными. Вплоть до времени выполнения не будет известно, поддерживают ли вызываемые динамические данные указанный член, переданы ли корректные параметры, правильно ли записано имя члена, и т.д. Таким образом, хотя это может показаться странным, следующий метод благополучно скомпилируется:

static void InvokeMembersOnDynamicData()

{

  dynamic textData1 = "Hello";

  Console.WriteLine(textData1.ToUpper());

  // Здесь можно было бы ожидать ошибки на этапе компиляции!

  // Однако все компилируется нормально.

  Console.WriteLine(textData1.toupper());

  Console.WriteLine(textData1.Foo(10, "ee", DateTime.Now));

}

Обратите внимание, что во втором вызове WriteLine() предпринимается попытка обращения к методу по имени toupper() на динамическом элементе данных (при записи имени метода использовался неправильный регистр символов; оно должно выглядеть как ToUpper()). Как видите, переменная textData1 имеет тип string, а потому известно, что у этого типа отсутствует метод с именем, записанным полностью в нижнем регистре. Более того, тип string определенно не имеет метода по имени Foo(), который принимает параметры int, string и DataTime!

Тем не менее, компилятор C# ни о каких ошибках не сообщает. Однако если вызвать метод InvokeMembeгsOnDynamicData(), то возникнет ошибка времени выполнения с примерно таким сообщением:

Unhandled Exception : Microsoft.CSharp.RuntimeBinder.RuntimeBinderException:

'string' does not contain a definition for 'toupper'

Необработанное исключение: Microsoft.CSharp.RuntimeBinder.

RuntimeBinderException: string не содержит определения для toupper

Другое очевидное отличие между обращением к членам динамических и строго типизированных данных связано с тем, что когда к элементу динамических данных применяется операция точки, ожидаемое средство IntelliSense среды Visual Studio не активизируется. Взамен IDE-среда позволит вводить любое имя члена, какое только может прийти вам на ум.

Отсутствие возможности доступа к средству IntelliSense для динамических данных должно быть понятным. Тем не менее, как вы наверняка помните, это означает необходимость соблюдения предельной аккуратности при наборе кода C# для таких элементов данных. Любая опечатка или символ в неправильном регистре внутри имени члена приведет к ошибке времени выполнения, в частности к генерации исключения типа RuntimeBinderException.

Класс RuntimeBinderException представляет ошибку, которая будет сгенерирована при попытке обращения к несуществующему члену динамического типа данных (как в случае toupper() и Foo()). Та же самая ошибка будет инициирована, если для члена, который существует, указаны некорректные данные параметров.

Поскольку динамические данные настолько изменчивы, любые обращения к членам переменной, объявленной с ключевым словом dynamic, могут быть помещены внутрь подходящего блока try/catch для элегантной обработки ошибок:

static void InvokeMembersOnDynamicData()

{

  dynamic textData1 = "Hello";

  try

  {

    Console.WriteLine(textData1.ToUpper());

    Console.WriteLine(textData1.toupper());

    Console.WriteLine(textData1.Foo(10, "ee", DateTime.Now));

  }

  catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex)

  {

    Console.WriteLine(ex.Message);

  }

}

Если вызвать метод InvokeMembersOnDynamicData() снова, то можно заметить, что вызов ToUpper() (обратите внимание на заглавные буквы "Т" и "U") работает корректно, но затем на консоль выводится сообщение об ошибке:

HELLO

'string' does not contain a definition for 'toupper'

string не содержит определение для toupper

Конечно, процесс помещения всех динамических обращений к методам в блоки try/catch довольно утомителен. Если вы тщательно следите за написанием кода и передачей параметров, тогда поступать так необязательно. Однако перехват исключений удобен, когда вы заранее не знаете, присутствует ли интересующий член в целевом типе.

Область использования ключевого слова dynamic

Вспомните, что неявно типизированные данные (объявленные с ключевым словом var) возможны только для локальных переменных в области действия члена. Ключевое слово var никогда не может использоваться с возвращаемым значением, параметром или членом класса/структуры. Тем не менее, это не касается ключевого слова dynamic. Взгляните на следующее определение класса:

namespace DynamicKeyword

{

  class VeryDynamicClass

  {

    // Динамическое поле.

    private static dynamic _myDynamicField;

    // Динамическое свойство.

    public dynamic DynamicProperty { get; set; }

    // Динамический тип возврата и динамический тип параметра.

    public dynamic DynamicMethod(dynamic dynamicParam)

    {

1 ... 218 219 220 221 222 223 224 225 226 ... 407
На этой странице вы можете бесплатно читать книгу Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен бесплатно.

Оставить комментарий