Для того чтобы стало понятно, насколько тип 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
. В противном случае выполнение программы завершилось бы неудачно.