Мне интересно, какой будет общая рекомендация (атрибут, интерфейс, абстрактный класс или их комбинация) для следующей реализации:
/// <summary>
/// Loads class specific information into a list for serialization. The class must extend PlugIn.
/// The filenames parameter is passed from a FileDialog.
/// </summary>
/// <param name="filenames">Accepts any number of filenames with fully qualified paths.</param>
public static void ExtractPlugInData(params string[] filenames)
{
List<Type> l;
foreach (string f in filenames)
{
Assembly a = Assembly.LoadFrom(f);
// lambda expression selects any class within a library extending the abstract PlugIn class
l = a.GetTypes().Where(type => typeof(PlugIn).IsAssignableFrom(type)).ToList<Type>();
if (l.Count > 0)
// write data to serializable class
WritePlugInData(f , l);
else
// throw exception
WriteLine("{0} :: No PlugIn Data Found" , a.FullName);
}
}
Я понимаю, что у каждого метода есть свои преимущества и недостатки. Очевидно, что атрибуты требуют некоторого отражения (как и абстрактное расширение и реализация интерфейса). Абстрактный класс берет наше единственное базовое наследование, и любые будущие изменения в интерфейсе могут сломать любые существующие плагины. Так что, на мой взгляд, это недостатки.
Производительность не является проблемой (если нет чего-то, чего я не вижу), поскольку любое отражение выполняется только один раз, когда извлекается квалифицированный класс. Ключевыми частями данных, которые сохраняются, является имя плагина («MyPlugIn»), пространство имен («SuperPlugIn.PlugInClass») и путь запуска для DLL. Прямо сейчас, с абстрактным классом PlugIn, расширение свойств принудительно. Это более или менее тот же результат, если мы реализуем интерфейс (IPlugIn).
Мы разрешаем конечным пользователям создавать собственные плагины. С помощью плагинов, которые мы пишем самостоятельно, легко обучить и реализовать необходимую структуру для нашего приложения, например, для создания квалифицированного класса. Однако я также рассматриваю трудности или неудобства для конечного пользователя в случае серьезных изменений.
Все комментарии, предложения и вопросы приветствуются !!
Примечание: спасибо Джону Скиту за лямбда-выражение во фрагменте. :)
РЕДАКТИРОВАТЬ: я должен был отметить в начале, что это предназначено, чтобы быть независимым от платформы (то есть моно).
ОБНОВЛЕНИЕ: Основываясь на превосходных рекомендациях, комментариях и ссылках ниже, сочетание атрибутов и интерфейсов является наилучшим подходом. Атрибуты позволяют загружать сборку и довольно безопасно проверять необходимую информацию и реализации без создания экземпляров классов / объектов плагина. Это идеально в ситуациях, когда сторонним или конечным пользователям разрешено создавать собственные плагины. Мы можем проверить, что правильная реализация контракта находится там, где атрибут говорит, что он должен быть. Мы можем проверить наличие необходимых зависимостей и ресурсов и предупредить разработчика о любых проблемах, прежде чем что-либо будет создано.