Портирование кода из Reflection на использование интерфейса, некоторые API не реализованы - PullRequest
0 голосов
/ 23 июня 2010

У меня есть старый код плагина C #, который был реализован строго с помощью Reflection.Исправляя некоторые проблемы совместимости C # 2.0 -> 4.0 ( «Загрузка из удаленного источника» ), я решил избавиться от старого кода отражения и заменить его интерфейсом.Интерфейс необходим, потому что теперь плагины должны быть загружены в их собственный домен приложений, а затем распределены по ним.

Я могу просмотреть источник для сотен плагинов и просто заставить их всех реализовать новый интерфейс IPlugin спростой поиск и замена.Это прекрасно работает за исключением одного критического места. И я ищу легкий выход.

Метод RunPlugin() может быть реализован одним из двух способов, но не обоими одновременно: либо с аргументом, либо без.Если я включу это в интерфейс, мне придется реализовать оба в каждом из плагинов.Вызывающая сторона вызывает метод с одним или без аргументов на основе , который реализован .Вызывающая сборка теперь выясняет это с помощью отражения.

Чтобы избежать того, что я могу создать класс-оболочку для плагинов, эта оболочка реализует интерфейс, но тогда мне придется интенсивно редактировать каждый из плагинов, чтобы включитьпереопределение для каждого из многих методов API.

Пример кода (это не обязательно работает! Сейчас все в процессе перехода!):

Интерфейс (пример):

// In IPlugin.cs / IPlugin.dll
namespace Plugin
{
    public interface IPlugin
    {
           // Many, many happy API things like this...
           void SetupOptions(Hashtable options);
           // (examples elided)

           // And then these two... either one or the other
           // is implemented, but not both.
           XmlDocument RunPlugin(Updater u);
           XmlDocument RunPlugin();
    }
}

Вызываемая сборка ... У меня есть лотов из них.Я могу добавить ": IPlugin" довольно легко.Очевидно, что он не скомпилируется, потому что он не реализует один аргумент RunPlugin().

namespace Plugin
{
      public class FileMaintenance : IPlugin
      {
          public void SetupOptions(Hashtable options)
          {  // Code elided
          } 

          public XmlDocument RunPlugin()
          {  // Code elided
          }
      }
}

И, наконец, вызывающий код.Вот как это выглядело в коде отражения:

public XmlDocument RunPlugin(PluginRunner.Updater u)
{
    Type [] paramTypes = new Type [0];
    MethodInfo runInfo = repType.GetMethod("RunPlugin", paramTypes);
    if (runInfo == null)
    {
        paramTypes = new Type [1];
        paramTypes[0] = u.GetType();
        runInfo = repType.GetMethod("RunPlugin", paramTypes);
        if (runInfo == null)
            throw new Exception;
    }
    Object[] parameters;
    if ( paramTypes.Length == 0)
        parameters = new Object[0];
    else
    {
        parameters = new Object[1];
        parameters[0] = u;
    }
    Object returnVal;
    try 
    {
        returnVal = runInfo.Invoke(repObj,parameters);
    }
    catch (Exception e)
    {
            }
         // Rest of code omitted
 }

Помните: я ищу хороший баланс между правильным способом исправить этот старый код и минимальным количествомредактирование кода вручную.

Ответы [ 4 ]

3 голосов
/ 23 июня 2010

Я бы выступил за создание двух дополнительных интерфейсов:

public interface IRunnablePlugin : IPlugin
{
    XmlDocument RunPlugin();
}

public interface IParamRunnablePlugin : IPlugin
{
    XmlDocument RunPlugin(object parameter);
}

Тогда пусть все ваши плагины реализуют один или другой. Единственный раз, когда вам придется проводить различие, это когда вы звоните RunPlugin. В остальное время вы можете называть это простым IPlugin.

Например, чтобы выполнить вызов, вы должны сделать что-то вроде этого:

IPlugin plugin = ...;

IRunnablePlugin runnable = plugin as IRunnablePlugin;
IRunnableParamPlugin param = plugin as IRunnableParamPlugin;

XmlDocument output;

if(param != null)
{
    output = param.RunPlugin(parameter);
}
else if(runnable != null)
{
    output = runnable.RunPlugin();
}
else
{
    throw new InvalidOperationException();
}

Обратите внимание, что технически 1011 * не имеет никаких ограничений, позволяющих разработчику реализовать только одну из версий, но, надеюсь, это не должно быть проблемой. В этом коде вы сначала проверяете наличие параметризованной версии, затем версии без параметров, а затем выдает исключение, если ничего не найдено.

1 голос
/ 23 июня 2010

К сожалению, использование одного или другого RunPlugin метода было лечением Ахилла, которое было добавлено к этому проекту от getgo.Это было нестабильное проектное решение, с которым будет трудно иметь дело.

Одна возможность - добавить обе перегрузки к IPlugin, и позволить плагинам указать ту, которую они не реализовали, бросив NotImplementedException.Это, вероятно, не очень-то вам выгодно, возможно, ничего не купит.

Другая возможность - два интерфейса, IPlugin и IPluginWithArgs, и определить, какой интерфейс реализует данный плагин, и перейти оттуда.Также ужасно.

Другая возможность - это метод расширения public static void RunPlugin(this IPlugin plugin), который в основном скрывает и убирает отражение, которое вы уже получили.Я тоже не думаю, что это тебя что-то покупает.

0 голосов
/ 23 июня 2010

Итак, вы говорите ...

Чтобы избежать этого, я могу создать класс-оболочку для плагинов, эту оболочку реализует интерфейс, но тогда мне придется интенсивно редактировать каждый из плагины для включения переопределения для каждого из множества методов API.

Я не совсем уверен, что понимаю проблему с классом-оберткой, но мы можем думать о разных вещах.

Класс-оболочка, о котором я думаю, взял бы интерфейс с нулевыми параметрами и сделал бы его похожим на интерфейс с одним параметром. Этот параметр будет просто игнорироваться при вызове «RunPlugin».

Это удалит любые ветвления по типам для ваших интерфейсов, и вам нужно будет только создать этот один класс - он может обернуть любой экземпляр интерфейса без параметров. Так что не должно быть никакого специального кода, необходимого для каждого плагина. Просто создайте оболочку при создании экземпляра плагина.

(Кстати, это шаблон адаптера .)

0 голосов
/ 23 июня 2010

У вас может быть один базовый интерфейс, например:

public interface IPlugin {
           // Many, many happy API things like this...
           void SetupOptions(Hashtable options);
           // (examples elided)

           XmlDocument RunPlugin();
}

//
// And another interface extending the IPlugin that defines the update version
//

public interface IUpdaterPlugin : IPlugin {
           XmlDocument RunPlugin(Updater u);

}

Для запуска соответствующего плагина ...

if( plugin is IUpdaterPlugin)
    plugin.RunPlugin(updater);
else if(plugin is IPlugin)
    plugin.RunPlugin();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...