Полное руководство. С# 4.0 - Шилдт Герберт
Шрифт:
Интервал:
Закладка:
bool b = val;то возникнет ошибка при выполнении из-за отсутствия неявного преобразования типаint (который оказывается типом переменной val во время выполнения) в тип bool.Поэтому данная строка кода приведет к ошибке при выполнении, хотя она и будетскомпилирована безошибочно.Прежде чем оставить данный пример программы, попробуйте поэкспериментировать с ней. В частности, измените тип переменных str и val на object, а затемпопытайтесь скомпилировать программу еще раз. В итоге появятся ошибки при компиляции, поскольку тип object не поддерживает действия, выполняемые над обеими переменными, что и будет обнаружено во время компиляции. В этом, собственно,и заключается основное отличие типов object и dynamic. Несмотря на то что обатипа могут использоваться для ссылки на объект любого другого типа, над переменной типа object можно производить только те действия, которые поддерживаютсятипом object. Если же вы используете тип dynamic, то можете указать какое угоднодействие, при условии что это действие поддерживается конкретным объектом, на который делается ссылка во время выполнения.Для того чтобы стало понятно, насколько тип dynamic способен упростить решение некоторых задач, рассмотрим простой пример его применения вместе с рефлексией. Как пояснялось в главе 17, чтобы вызвать метод для объекта класса, получаемогово время выполнения с помощью рефлексии, можно, в частности, обратиться к методуInvoke(). И хотя такой способ оказывается вполне работоспособным, нужный методнамного удобнее вызвать по имени в тех случаях, когда его имя известно. Например,вполне возможна такая ситуация, когда в некоторой сборке содержится конкретныйкласс, поддерживающий методы, имена и действия которых заранее известны. Но поскольку эта сборка подвержена изменениям, то приходится постоянно убеждаться в том,что используется последняя ее версия. Для проверки текущей версии сборки можно,например, воспользоваться рефлексией, сконструировать объект искомого класса, а затем вызвать методы, определенные в этом классе. Теперь эти методы можно вызвать поимени с помощью типа dynamic, а не метода Invoke(), поскольку их имена известны.Разместите сначала приведенный ниже код в файле с именем MyClass.cs. Этоткод будет динамически загружаться посредством рефлексии.
public class DivBy { public bool IsDivBylint a, int b) { if((a % b) == 0) return true; return false; }public bool IsEven(int a) { if((a % 2) == 0) return true; return false;}
}Затем скомпилируйте этот файл в библиотеку DLL под именем MyClass.dll.Если вы пользуетесь компилятором командной строки, введите в командной строкеследующее.
csc /t:library MyClass.csДалее составьте программу, в которой применяется библиотека MyClass.dll, какпоказано ниже.
// Использовать тип dynamic вместе с рефлексией.using System;using System.Reflection;
class DynRefDemo { static void Main() { Assembly asm = Assembly.LoadFrom("MyClass.dll"); Type[] all = asm.GetTypes(); // Найти класс DivBy. int i; for(i = 0; i < all.Length; i++) if(all[i].Name == "DivBy") break; if(i == all.Length) { Console.WriteLine("Класс DivBy не найден в сборке."); return; } Type t = all[i]; //А теперь найти используемый по умолчанию конструктор. ConstructorInfо[] ci = t.GetConstructors(); int j; for(j = 0; j < ci.Length; j++) if(ci[j].GetParameters().Length == 0) break; if(j == ci.Length) { Console.WriteLine("Используемый по умолчанию конструктор не найден."); return; } // Создать объект класса DivBy динамически. dynamic obj = ci[j].Invoke (null); // Далее вызвать по имени методы для переменной obj. Это вполне допустимо, // поскольку переменная obj относится к типу dynamic, а вызовы методов // проверяются на соответствие типов во время выполнения, а не компиляции. if(obj.IsDivBy(15, 3)) Console.WriteLine("15 делится нацело на 3."); else Console.WriteLine("15 HE делится нацело на 3."); if(obj.IsEven(9)) Console.WriteLine("9 четное число."); else Console.WriteLine("9 HE четное число.");}
}Как видите, в данной программе сначала динамически загружается библиотекаMyClass.dll, а затем используется рефлексия для построения объекта класса DivBy.Построенный объект присваивается далее переменной obj типа dynamic. А раз так,то методы IsDivBy() и IsEven() могут быть вызваны для переменной obj по имени,а не с помощью метода Invoke(). В данном примере это вполне допустимо, поскольку переменная obj на самом деле ссылается на объект класса DivBy. В противном случае выполнение программы завершилось бы неудачно.Приведенный выше пример сильно упрощен и несколько надуман. Тем не менее оннаглядно показывает главное преимущество, которое дает тип dynamic в тех случаях,когда типы получаются во время выполнения. Когда характеристики искомого типа,в том числе методы, операторы, поля и свойства, заранее известны, эти характеристики могут быть получены по имени с помощью типа dynamic, как следует из приведенного выше примера. Благодаря этому код становится проще, короче и понятнее.Применяя тип dynamic, следует также иметь в виду, что при компиляции программы тип dynamic фактически заменяется объектом, а для описания его применения вовремя выполнения предоставляется соответствующая информация. И поскольку типdynamic компилируется в тип object для целей перегрузки, то оба типа dynamicи object расцениваются как одно и то же. Поэтому при компиляции двух следующихперегружаемых методов возникнет ошибка.
static void f(object v) { // ... }static void f(dynamic v) { // ... } // Ошибка!И последнее замечание: тип dynamic поддерживается компонентом DLR (DynamicLanguage Runtime — Средство создания динамических языков во время выполнения),внедренным в .NET 4.0.## Возможность взаимодействия с моделью СОМВ версии C# 4.0 внедрены средства, упрощающие возможность взаимодействияс неуправляемым кодом, определяемым моделью компонентных объектов (СОМ)и применяемым, в частности, в COM-объекте Office Automation. Некоторые из этихсредств, в том числе тип dynamic, именованные и необязательные свойства, пригодныдля применения помимо возможности взаимодействия с моделью СОМ. Тема моделиСОМ вообще и COM-объекта Office Automation в частности весьма обширна, а порой идовольно сложна, чтобы обсуждать ее в этой книге. Поэтому возможность взаимодействия с моделью СОМ выходит за рамки данной книги.Тем не менее две особенности, имеющие отношение к возможности взаимодействия с моделью СОМ, заслуживают краткого рассмотрения в этом разделе. Первая изних состоит в применении индексированных свойств, а вторая — в возможности передавать аргументы значения тем COM-методам, которым требуется ссылка.Как вам должно быть уже известно, в C# свойство обычно связывается только с однимзначением с помощью одного из аксессоров get или set. Но совсем иначе дело обстоит со свойствами модели СОМ. Поэтому, начиная с версии C# 4.0, в качестве выхода изэтого затруднительного положения во время работы с COM-объектом появилась возможность пользоваться индексированным свойством для доступа к COM-свойству, имеющему несколько параметров. С этой целью имя свойства индексируется, почти также, как это делается с помощью индексатора. Допустим, что имеется объект myXLApp,который относится к типу Microsoft.Office.Interop.Execl.Application.В прошлом для установки строкового значения "OК" в ячейках С1-СЗ электроннойтаблицы Excel можно было бы воспользоваться оператором, аналогичным следующему.
myXLapp.get_Range("C1", "С3").set_Value(Type.Missing, "OK");В этой строке кода интервал ячеек электронной таблицы получается при вызовеметода get_Range(), для чего достаточно указать начало и конец интервала. А значения задаются при вызове метода set_Value(), для чего достаточно указать тип (чтоне обязательно) и конкретное значение. В этих методах используются свойства Range иValue, поскольку у обоих свойств имеются два параметра. Поэтому в прошлом к нимнельзя было обращаться как к свойствам, но приходилось пользоваться упомянутымивыше методами. Кроме того, аргумент Type.Missing служил в качестве обычного заполнителя, который передавался для указания на тип, используемый по умолчанию.Но, начиная с версии C# 4.0, появилась возможно переписать приведенный выше оператор, приведя его к следующей более удобной форме.