Язык программирования C#9 и платформа .NET5 - Эндрю Троелсен
Шрифт:
Интервал:
Закладка:
Добавление событий PostBuild
Откройте окно свойств проекта для CSharpSnapIn (щелкнув правой кнопкой мыши на имени проекта в окне Solution Explorer и выбрав в контекстном меню пункт Properties (Свойства)) и перейдите в нем на вкладку Build Events (События при компиляции). Щелкните на кнопке Edit Post-build (Редактировать события после компиляции) и затем щелкните на Macros>> (Макросы). Здесь вы можете видеть доступные для использования макросы, которые ссылаются на пути и/или имена файлов. Преимущество применения этих макросов в событиях, связанных с компиляцией, заключается в том, что они не зависят от машины и работают с относительными путями. Скажем, кто-то работает в каталоге по имени c-sharp-wfcodechapterl7. Вы можете работать в другом каталоге (вероятнее всего так и есть). За счет применения макросов инструмент MSBuild всегда будет использовать корректный путь относительно файлов *.csproj.
Введите в области PostBuild (После компиляции) следующие две строки:
copy $(TargetPath) $(SolutionDir)MyExtendableApp$(OutDir)$(TargetFileName) /Y
copy $(TargetPath) $(SolutionDir)MyExtendableApp$(TargetFileName) /Y
Сделайте то же самое для проекта VBSnapin, но здесь вкладка в окне свойств называется Compile (Компиляция) и на ней понадобится щелкнуть на кнопке Build Events (События при компиляции).
Когда показанные выше команды событий после компиляции добавлены, все сборки при каждой компиляции будут копироваться в каталог проекта и выходной каталог приложения MyExtendableApp.
Построение сборки CommonSnappableTypes.dll
Удалите стандартный файл класса Class1.cs из проекта CommonSnappableTypes, добавьте новый файл интерфейса по имени AppFunctionality.cs и поместите в него следующий код:
namespace CommonSnappableTypes
{
public interface IAppFunctionality
{
void DoIt();
}
}
Добавьте файл класса по имени CompanyInfoAttribute.cs и приведите его содержимое к такому виду:
using System;
namespace CommonSnappableTypes
{
[AttributeUsage(AttributeTargets.Class)]
public sealed class CompanyInfoAttribute : System.Attribute
{
public string CompanyName { get; set; }
public string CompanyUrl { get; set; }
}
}
Тип IAppFunctionality обеспечивает полиморфный интерфейс для всех оснасток, которые могут потребляться расширяемым приложением. Учитывая, что рассматриваемый пример является полностью иллюстративным, в интерфейсе определен единственный метод под названием DoIt().
Тип CompanyInfoAttribute — это специальный атрибут, который может применяться к любому классу, желающему подключиться к контейнеру. Как несложно заметить по определению класса, [CompanyInfо] позволяет разработчику оснастки указывать общие сведения о месте происхождения компонента.
Построение оснастки на C#
Удалите стандартный файл Class1.cs из проекта CSharpSnapIn и добавьте новый файл по имени CSharpModule.cs. Поместите в него следующий код:
using System;
using CommonSnappableTypes;
namespace CSharpSnapIn
{
[CompanyInfo(CompanyName = "FooBar", CompanyUrl = "www.FooBar.com")]
public class CSharpModule : IAppFunctionality
{
void IAppFunctionality.DoIt()
{
Console.WriteLine("You have just used the C# snap-in!");
}
}
}
Обратите внимание на явную реализацию интерфейса IAppFunctionality (см. главу 8). Поступать так необязательно; тем не менее, идея заключается в том, что единственной частью системы, которая нуждается в прямом взаимодействии с упомянутым интерфейсным типом, будет размещающее приложение. Благодаря явной реализации интерфейса IAppFunctionality метод DoIt() не доступен напрямую из типа CSharpModule.
Построение оснастки на Visual Basic
Теперь перейдите к проекту VBSnapIn. Удалите файл Class1.vb и добавьте новый файл по имени VBSnapIn.vb. Код Visual Basic столь же прост:
Imports CommonSnappableTypes
<CompanyInfo(CompanyName:="Chucky's Software", CompanyUrl:="www.ChuckySoft.com")>
Public Class VBSnapIn
Implements IAppFunctionality
Public Sub DoIt() Implements CommonSnappableTypes.IAppFunctionality.DoIt
Console.WriteLine("You have just used the VB snap in!")
End Sub
End Class
Как видите, применение атрибутов в синтаксисе Visual Basic требует указания угловых скобок (<>), а не квадратных ([]). Кроме того, для реализации интерфейсных типов заданным классом или структурой используется ключевое слово Implements.
Добавление кода для ExtendableApp
Последним обновляемым проектом является консольное приложение C# (MyExtendableApp). После добавления к решению консольного приложения MyExtendableApp и установки его как стартового проекта добавьте ссылку на проект CommonSnappableTypes, но не на CSharpSnapIn.dll или VbSnapIn.dll. Модифицируйте операторы using в начале файла Program.cs, как показано ниже:
using System;
using System.Linq;
using System.Reflection;
using CommonSnappableTypes;
Метод LoadExternalModule() выполняет следующие действия:
• динамически загружает в память выбранную сборку;
• определяет, содержит ли сборка типы, реализующие интерфейс IAppFunctionality;
• создает экземпляр типа, используя позднее связывание.
Если обнаружен тип, реализующий IAppFunctionality, тогда вызывается метод DoIt() и найденный тип передается методу DisplayCompanyData() для вывода дополнительной информации о нем посредством рефлексии.
static void LoadExternalModule(string assemblyName)
{
Assembly theSnapInAsm = null;
try
{
// Динамически загрузить выбранную сборку.
theSnapInAsm = Assembly.LoadFrom(assemblyName);
}
catch (Exception ex)
{
// Ошибка при загрузке оснастки
Console.WriteLine($"An error occurred loading the snapin: {ex.Message}");
return;
}
// Получить все совместимые c IAppFunctionality классы в сборке.
var theClassTypes = theSnapInAsm
.GetTypes()
.Where(t => t.IsClass && (t.GetInterface("IAppFunctionality") != null))
.ToList();
if (!theClassTypes.Any())
{
Console.WriteLine("Nothing implements IAppFunctionality!");
// Ни один класс не реализует IAppFunctionality!
}
// Создать объект и вызвать метод DoIt().
foreach (Type t in theClassTypes)
{
/// Использовать позднее связывание для создания экземпляра типа.
IAppFunctionality itfApp =
(IAppFunctionality) theSnapInAsm.CreateInstance(t.FullName, true);
itfApp?.DoIt();
// Отобразить информацию о компании.
DisplayCompanyData(t);
}
}
Финальная задача связана с отображением метаданных, предоставляемых атрибутом [CompanyInfo]. Создайте метод DisplayCompanyData(), который принимает параметр System.Туре:
static void DisplayCompanyData(Type t)
{
// Получить данные [CompanyInfo].
var compInfo = t
.GetCustomAttributes(false)
.Where(ci =>