Язык программирования C#9 и платформа .NET5
static void InvokeMembersOnDynamicData(){dynamic textData1 = "Hello";Console.WriteLine(textData1.ToUpper());<b> // Здесь можно было бы ожидать ошибки на этапе компиляции!</b><b> // Однако все компилируется нормально.</b>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/catchstatic 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()(обратите внимание на заглавные буквы "Т" и "U") работает корректно, но затем на консоль выводится сообщение об ошибке:ToUpper()HELLO'string' does not contain a definition for 'toupper'<i>string не содержит определение для toupper</i>Конечно, процесс помещения всех динамических обращений к методам в блоки
довольно утомителен. Если вы тщательно следите за написанием кода и передачей параметров, тогда поступать так необязательно. Однако перехват исключений удобен, когда вы заранее не знаете, присутствует ли интересующий член в целевом типе.try/catchОбласть использования ключевого слова dynamic
Вспомните, что неявно типизированные данные (объявленные с ключевым словом
) возможны только для локальных переменных в области действия члена. Ключевое словоvarникогда не может использоваться с возвращаемым значением, параметром или членом класса/структуры. Тем не менее, это не касается ключевого словаvar. Взгляните на следующее определение класса:dynamicnamespace DynamicKeyword{class VeryDynamicClass{<b> // Динамическое поле.</b>private static dynamic _myDynamicField;<b> // Динамическое свойство.</b>public dynamic DynamicProperty { get; set; }<b> // Динамический тип возврата и динамический тип параметра.</b>public dynamic DynamicMethod(dynamic dynamicParam){<b> // Динамическая локальная переменная.</b>