Однако из-за того, что в приложение не была добавлена ссылка на сборку
CarLibrary.dll
, использовать ключевое слово
using
для импортирования пространства имен
CarLibrary
нельзя, а значит невозможно и указывать тип
MiniVan
в операции приведения! Не забывайте, что смысл позднего связывания — создание экземпляров типов, о которых
на этапе компиляции ничего не известно. Учитывая сказанное, возникает вопрос: как вызывать методы объекта
MiniVan
, сохраненного в ссылке на
System.Object
? Ответ: конечно же, с помощью рефлексии.
Вызов методов без параметров
Предположим, что требуется вызвать метод
TurboBoost
объекта
MiniVan
. Вспомните, что упомянутый метод переводит двигатель в нерабочее состояние и затем отображает окно с соответствующим сообщением. Первый шаг заключается в получении объекта
MethodInf
о для метода
TurboBoost
посредством
Туре.GetMethod
. Имея результирующий объект
MethodInfо
, можно вызвать
MiniVan.TurboBoost
с помощью метода
Invoke
. Метод
MethodInfо.Invoke
требует указания всех параметров, которые подлежат передаче методу, представленному объектом
MethodInfо
. Параметры задаются в виде массива объектов
System.Object
(т.к. они могут быть самыми разнообразными сущностями).
Поскольку метод
TurboBoost
не принимает параметров, можно просто передать
null
(т.е. сообщить, что вызываемый метод не имеет параметров). Обновите метод
CreateUsingLateBinding
следующим образом:
static void CreateUsingLateBinding(Assembly asm)
{
try
{
// Получить метаданные для типа Minivan.
Type miniVan = asm.GetType("CarLibrary.MiniVan");
// Создать объект MiniVan на лету.
object obj = Activator.CreateInstance(miniVan);
Console.WriteLine($"Created a {obj} using late binding!");
// Получить информацию о TurboBoost.
MethodInfo mi = miniVan.GetMethod("TurboBoost");
// Вызвать метод (null означает отсутствие параметров).
mi.Invoke(obj, null);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
}
Теперь после запуска приложения вы увидите в окне консоли сообщение о том, что двигатель вышел из строя (
"Eek! Your engine block exploded!"
).
Вызов методов с параметрами
Когда позднее связывание нужно применять для вызова метода, ожидающего параметры, аргументы потребуется упаковать в слабо типизированный массив
object
. В версии класса
Car
с радиоприемником был определен следующий метод:
public void TurnOnRadio(bool musicOn, MusicMediaEnum mm)
принимает два параметра: булевское значение, которое указывает, должна ли быть включена музыкальная система в автомобиле, и перечисление, представляющее тип музыкального проигрывателя. Вспомните, что это перечисление определено так:
public enum MusicMediaEnum
{
musicCd, // 0
musicTape, // 1
musicRadio, // 2
musicMp3 // 3
}
Ниже приведен код нового метода класса
Program
, в котором вызывается
TurnOnRadio
. Обратите внимание на использование внутренних числовых значений перечисления
// Получить описание метаданных для типа SportsCar.
Type sport = asm.GetType("CarLibrary.SportsCar");
// Создать объект типа SportsCar.
object obj = Activator.CreateInstance(sport);
// Вызвать метод TurnOnRadio с аргументами.
MethodInfo mi = sport.GetMethod("TurnOnRadio");
mi.Invoke(obj, new object[] { true, 2 });
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
В идеале к настоящему времени вы уже видите отношения между рефлексией, динамической загрузкой и поздним связыванием. Естественно, помимо раскрытых здесь возможностей API-интерфейс рефлексии предлагает много дополнительных средств, но вы уже должны быть в хорошей форме, чтобы погрузиться в дальнейшее изучение.
Вас все еще может интересовать вопрос: когда описанные приемы должны применяться в разрабатываемых приложениях? Ответ прояснится ближе к концу главы, а пока мы займемся исследованием роли атрибутов .NET Core.
Роль атрибутов .NET
Как было показано в начале главы, одной из задач компилятора .NET Core является генерация описаний метаданных для всех определяемых типов и для типов, на которые имеются ссылки. Помимо стандартных метаданных, содержащихся в любой сборке, платформа .NET Core предоставляет программистам способ встраивания в сборку дополнительных метаданных с использованием атрибутов. Выражаясь кратко, атрибуты — это всего лишь аннотации кода, которые могут применяться к заданному типу (классу, интерфейсу, структуре и т.п.), члену (свойству, методу и т.д.), сборке или модулю.
Атрибуты .NET Core представляют собой типы классов, расширяющие абстрактный базовый класс
System.Attribute
. По мере изучения пространств имен .NET Core вы найдете много предопределенных атрибутов, которые можно использовать в своих приложениях. Более того, вы также можете строить собственные атрибуты для дополнительного уточнения поведения своих типов путем создания нового типа, производного от
Attribute
.
Библиотеки базовых классов .NET Core предлагают атрибуты в различных пространствах имен. В табл. 17.3 описаны некоторые (безусловно, далеко не все) предопределенные атрибуты.