Для того чтобы стало понятно, насколько тип dynamicспособен упростить решение некоторых задач, рассмотрим простой пример его применения вместе с рефлексией. Как пояснялось в главе 17, чтобы вызвать метод для объекта класса, получаемого во время выполнения с помощью рефлексии, можно, в частности, обратиться к методу Invoke(). И хотя такой способ оказывается вполне работоспособным, нужный метод намного удобнее вызвать по имени в тех случаях, когда его имя известно. Например, вполне возможна такая ситуация, когда в некоторой сборке содержится конкретный класс, поддерживающий методы, имена и действия которых заранее известны. Но поскольку эта сборка подвержена изменениям, то приходится постоянно убеждаться в том, что используется последняя ее версия. Для проверки текущей версии сборки можно, например, воспользоваться рефлексией, сконструировать объект искомого класса, а затем вызвать методы, определенные в этом классе. Теперь эти методы можно вызвать по имени с помощью типа dynamic, а не метода Invoke(), поскольку их имена известны.
Разместите сначала приведенный ниже код в файле с именем MyClass.cs. Этот код будет динамически загружаться посредством рефлексии.
public class DivBy {
public bool IsDivBy(int 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];
//А теперь найти используемый по умолчанию конструктор.
ConstructorInfo[] 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 НЕ четное число.");
}
}
Как видите, в данной программе сначала динамически загружается библиотека MyClass.dll, а затем используется рефлексия для построения объекта класса DivBy. Построенный объект присваивается далее переменной objтипа dynamic. А раз так, то методы IsDivBy()и IsEven()могут быть вызваны для переменной objпо имени, а не с помощью метода Invoke(). В данном примере это вполне допустимо, поскольку переменная objна самом деле ссылается на объект класса DivBy. В противном случае выполнение программы завершилось бы неудачно.
Приведенный выше пример сильно упрощен и несколько надуман. Тем не менее он наглядно показывает главное преимущество, которое дает тип dynamicв тех случаях, когда типы получаются во время выполнения. Когда характеристики искомого типа, в том числе методы, операторы, поля и свойства, заранее известны, эти характеристики могут быть получены по имени с помощью типа dynamic, как следует из приведенного выше примера. Благодаря этому код становится проще, короче и понятнее.
Читать дальше