繁体   English   中英

将代码从反射移植到使用接口,某些 API 未实现

[英]Porting code from Reflection to using an Interface, some API's not implemented

我有一些旧的 C# 插件代码,它们是用反射严格实现的。 在修复一些 C# 2.0 -> 4.0 兼容性问题( “从远程源加载” )时,我决定去掉旧的反射代码并用接口替换它。 需要该接口是因为插件现在需要加载到它们自己的 AppDomain 中,然后进行编组。

我可以浏览数百个插件的源代码,并通过简单的搜索和替换让它们全部实现新的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
 }

请记住:我正在寻找修复旧代码的正确方法和手动编辑最少代码之间的良好平衡。

我主张创建两个额外的接口:

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();
}

请注意,技术上没有限制,允许开发人员仅实现其中一个版本,但希望这不会成为问题。 在这段代码中,您首先检查参数化版本是否存在,然后是无参数版本,如果没有找到则抛出异常。

不幸的是,使用一种或多种RunPlugin方法是从一开始就添加到此设计中的致命弱点。 这是一个不稳定的设计决定,处理起来会很困难。

一种可能性是将两个重载都添加到IPlugin ,并让插件通过抛出NotImplementedException指示它们没有实现的那个。 那可能不会给你带来太多好处,它可能不会给你带来任何东西。

另一种可能性是两个接口, IPluginIPluginWithArgs ,并检测给定插件正在实现哪个接口并从那里开始。 也丑。

另一种可能性是扩展方法public static void RunPlugin(this IPlugin plugin) ,它基本上隐藏和整理你已经开始的反射。 我也不认为这能给你带来什么。

你可以有一个像这样的基本界面:

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();

所以你说...

为了避免我可以为插件创建一个包装器类,该包装器实现了接口,但是我必须大量编辑每个插件以包含对 API 的许多方法中的每一个的覆盖。

我不太确定我是否理解包装类的问题,但我们可能正在考虑不同的事情。

我正在考虑的包装器类将采用具有零参数的接口,并使其看起来像具有一个参数的接口。 调用“RunPlugin”时,该参数将被忽略。

这将删除您的接口类型的任何分支,您只需要创建这一类 - 它可以包装无参数接口的任何实例。 所以每个插件都不应该有任何特殊的代码。 只需在创建插件实例时创建包装器即可。

(顺便说一下,这是适配器模式。)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM