Традиционная постановка задачи позднего связывания может быть проиллюстрирована на следующем примере. Предположим, имеется программа, позволяющая выполнять обработку каких-либо файлов. Для обработки используются классы, реализующие интерфейс FileProcessor. Список типов обрабатываемых программой файлов и соответствующих им классов-обработчиков постоянно пополняется. Сборки, содержащие новые классы-обработчики, помещаются в отдельную папку, а программа-обработчик файлов должна уметь использовать всё новые и новые классы-обработчики без перекомпиляции.
Интерфейс класса-обработчика будет иметь единственную функцию, определяющую, может ли данный класс обработать данный файл. Поместим интерфейс в отдельную сборку. На неё будут ссылаться конкретные классы-обработчики.
Интерфейс-обработчик:
// compile with csc /t:library FileProcessor.cs
using System;
namespace FileProc
{ interface IFileProcessor
{ bool IsSupport(string filePath);
}
}
Теперь определим пару классов обработчиков. Пусть один из них будет работать с текстовыми файлами, второй – с картинками в формате ВMP. При этом каждый будет реализовывать интерфейс-обработчик, и будет помещён в отдельную сборку, ссылающуюся на сборку с интерфейсом.
Обработчик текстовых файлов:
// compile with csc /t:library TextProcessor.cs /r:FileProcessor.cs
В самом приложении, осуществляющем позднее связывание с классами-обработчиками, просто загружаются все сборки из каталога приложения. Затем запрашиваются все типы из каждой сборки, и среди типов ищутся те, что унаследованы от абстрактного класса-обработчика. После того, как нужный тип найден, создаётся его экземпляр и вызывается метод IsSupport.
// compile with csc LateBind.cs /r:FileProcessor.cs
using System;
using System.IO;
using System.Reflection;
using FileProc;
namespace LateBind
{ class Class1
{ [STAThread] //Атрибут
static void Main(string[] args)
{ if(args.Length < 1)
return;
string[] files = Directory.GetFiles(
System.Environment.CurrentDirectory, "*.dll");
foreach (string file in files)
{ Assembly assembly = Assembly.LoadFrom(file);
Type[] types = assembly.GetTypes();
foreach (Type type in types)
{ if (typeof(IFileProcessor).IsAssignableFrom(type)
&& ! type.IsAbstract
)
{ IFileProcessor processor
= (IFileProcessor)Activator.CreateInstance(type);
if (processor.IsSupport(args[0]))
{ Console.WriteLine(
"Assembly {0} can process file {1}."
, assembly.GetName().Name, args[0]);
Console.ReadLine();
return;
}
}
}
}
Console.WriteLine("Can't process file {0}.", args[0]);
Console.ReadLine();
}
}
}
Теперь, если появится необходимость обрабатывать файлы, например, формата VRML, не потребуется дорабатывать и перекомпилировать приложение. Достаточно будет разработать соответствующий класс-обработчик VRMLProcessor и поместить его в ту же папку, что и остальные классы-обработчики.