Атрибут, интерфейс или абстрактный класс - PullRequest
4 голосов
/ 26 января 2010

Мне интересно, какой будет общая рекомендация (атрибут, интерфейс, абстрактный класс или их комбинация) для следующей реализации:

    /// <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).

Мы разрешаем конечным пользователям создавать собственные плагины. С помощью плагинов, которые мы пишем самостоятельно, легко обучить и реализовать необходимую структуру для нашего приложения, например, для создания квалифицированного класса. Однако я также рассматриваю трудности или неудобства для конечного пользователя в случае серьезных изменений.

Все комментарии, предложения и вопросы приветствуются !!

Примечание: спасибо Джону Скиту за лямбда-выражение во фрагменте. :)

РЕДАКТИРОВАТЬ: я должен был отметить в начале, что это предназначено, чтобы быть независимым от платформы (то есть моно).

ОБНОВЛЕНИЕ: Основываясь на превосходных рекомендациях, комментариях и ссылках ниже, сочетание атрибутов и интерфейсов является наилучшим подходом. Атрибуты позволяют загружать сборку и довольно безопасно проверять необходимую информацию и реализации без создания экземпляров классов / объектов плагина. Это идеально в ситуациях, когда сторонним или конечным пользователям разрешено создавать собственные плагины. Мы можем проверить, что правильная реализация контракта находится там, где атрибут говорит, что он должен быть. Мы можем проверить наличие необходимых зависимостей и ресурсов и предупредить разработчика о любых проблемах, прежде чем что-либо будет создано.

Ответы [ 3 ]

2 голосов
/ 26 января 2010

Вы хотите, чтобы ваши конечные пользователи писали плагины? Я не думаю, что это очень хорошая идея, если ваши конечные пользователи не программисты.

На этот раз я буду кратким, потому что это довольно большой honkin ' dupe :

Редактировать: Для моно, проверьте Моно. Аддинс .

1 голос
/ 26 января 2010

Я, вероятно, склонен использовать атрибуты.Расширение системы базовых классов с помощью метаданных - это как раз то, для чего они нужны, и высказывание «этот класс является плагином», безусловно, соответствует этому требованию.

1 голос
/ 26 января 2010

Assembly.GetTypes - очень дорогой звонок, и я бы по возможности его избегал. (Время запуска приложения имеет значение)

Более быстрый способ сделать это, вероятно, (я не тестировал) атрибут уровня сборки, который будет использоваться следующим образом:

[assembly: PluginClass(typeof(MyPlugin), more info)]

Затем вы можете позвонить GetCustomAttributes на Assembly, что, вероятно, будет намного быстрее, чем GetTypes.

Использование LINQ:

filenames.SelectMany(f => 
        Assembly.LoadFrom(f).GetCustomAttributes(typeof(PluginClassAttribute), true)
        .Cast<PluginClassAttribute>()
        .Select(a => a.PluginType)
).ToList();
...