简体   繁体   English

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

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

I have some old C# plugin code that was implemented strictly with Reflection.我有一些旧的 C# 插件代码,它们是用反射严格实现的。 In fixing some C# 2.0 -> 4.0 compatibility issues ( "Load from Remote Source" ) I've decided to get rid of the old reflection code and replace it with an interface.在修复一些 C# 2.0 -> 4.0 兼容性问题( “从远程源加载” )时,我决定去掉旧的反射代码并用接口替换它。 The interface is needed because the Plugins now need to be loaded into their own AppDomain and then marshalled across.需要该接口是因为插件现在需要加载到它们自己的 AppDomain 中,然后进行编组。

I can go through the source for hundreds of plugins and simply make them all implement the new IPlugin interface with a simple search-and-replace.我可以浏览数百个插件的源代码,并通过简单的搜索和替换让它们全部实现新的IPlugin界面。 This works nicely except in one crucial place.除了在一个关键的地方,这很好用。 And I'm looking for an easy out.我正在寻找一个轻松的地方。

The method RunPlugin() can be implemented in one of two ways, but never both: Either with an argument or without.方法RunPlugin()可以通过以下两种方式之一实现,但不能同时实现:带参数或不带参数。 If I include this in the interface, I'd have to implement both in each of the plugins.如果我将其包含在界面中,则必须在每个插件中都实现。 The caller calls the one or no argument method based on which one is implemented .调用者根据实现的方法调用单参数方法或无参数方法。 The calling assembly figures this out now by reflection.调用程序集现在通过反射来解决这个问题。

To avoid that I can create a wrapper class for the plugins, that wrapper implements the interface, but then I'd have to heavily edit each of the plugins to include an override for each of the API's many methods.为了避免我可以为插件创建一个包装器类,该包装器实现了接口,但是我必须大量编辑每个插件以包含对 API 的许多方法中的每一个的覆盖。

Some sample code (this doesn't necessarily work! It's all in transition right now!):一些示例代码(这不一定有效!现在都在转换中!):

The interface (sample):界面(示例):

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

The called Assembly... I have lots of these.所谓的大会......我有很多这样的。 I can add the ": IPlugin" fairly easily.我可以很容易地添加“:IPlugin”。 This won't compile, obviously, because it doesn't implement the one-argument RunPlugin() .显然,这不会编译,因为它没有实现单参数RunPlugin()

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

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

And finally, the calling code.最后,调用代码。 This is actually how it used to look, back in the reflection code:这实际上是它过去的样子,回到反射代码中:

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
 }

Remember: I'm looking for a nice balance between the right way to fix this old code, and doing the minimal amount of editing code by hand.请记住:我正在寻找修复旧代码的正确方法和手动编辑最少代码之间的良好平衡。

I would advocate creating two additional interfaces:我主张创建两个额外的接口:

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

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

Then have all of your plugins implement one or the other.然后让您的所有插件实现其中一个。 The only time you'll have to make a distinction is when actualling calling RunPlugin .您唯一需要区分的时间是在实际调用RunPlugin All other times you can refer to it as a simple IPlugin .所有其他时候,您可以将其称为简单的IPlugin

For example, to perform the call, you'd do something like this:例如,要执行调用,您需要执行以下操作:

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

Note that there is technically no limitation that permits the developer to implement only one of the versions, but that should hopefully not be an issue.请注意,技术上没有限制,允许开发人员仅实现其中一个版本,但希望这不会成为问题。 In this code, you're checking for the presence of the parameterized version first, then the parameterless version, then throwing an exception if neither is found.在这段代码中,您首先检查参数化版本是否存在,然后是无参数版本,如果没有找到则抛出异常。

Unfortunately having one or the other RunPlugin methods was the Achilles heal that got added to this design from the getgo.不幸的是,使用一种或多种RunPlugin方法是从一开始就添加到此设计中的致命弱点。 That was a precarious design decision, it's going to be hard to deal with it.这是一个不稳定的设计决定,处理起来会很困难。

One possibility is to add both overloads to IPlugin , and let plugins indicate the one they didn't implement by throwing NotImplementedException .一种可能性是将两个重载都添加到IPlugin ,并让插件通过抛出NotImplementedException指示它们没有实现的那个。 That probably doesn't buy you much, it might not buy you anything.那可能不会给你带来太多好处,它可能不会给你带来任何东西。

Another possibility is two interfaces, IPlugin and IPluginWithArgs , and detect which interface a given plugin is implementing and go from there.另一种可能性是两个接口, IPluginIPluginWithArgs ,并检测给定插件正在实现哪个接口并从那里开始。 Also ugly.也丑。

Another possibility is an extension method public static void RunPlugin(this IPlugin plugin) that basically hides and tidies up the reflection you've already got going.另一种可能性是扩展方法public static void RunPlugin(this IPlugin plugin) ,它基本上隐藏和整理你已经开始的反射。 I don't think this buys you anything either.我也不认为这能给你带来什么。

You could have one base interface like so:你可以有一个像这样的基本界面:

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

}

To run the appropriate plugin...要运行适当的插件...

if( plugin is IUpdaterPlugin)
    plugin.RunPlugin(updater);
else if(plugin is IPlugin)
    plugin.RunPlugin();

So, you say...所以你说...

To avoid that I can create a wrapper class for the plugins, that wrapper implements the interface, but then I'd have to heavily edit each of the plugins to include an override for each of the API's many methods.为了避免我可以为插件创建一个包装器类,该包装器实现了接口,但是我必须大量编辑每个插件以包含对 API 的许多方法中的每一个的覆盖。

I'm not quite sure I understand the problem with the wrapper class, but we might be thinking about different things.我不太确定我是否理解包装类的问题,但我们可能正在考虑不同的事情。

The wrapper class I'm thinking about would take the interface with zero parameters and make it look like the interface with one parameter.我正在考虑的包装器类将采用具有零参数的接口,并使其看起来像具有一个参数的接口。 That parameter would simply be ignored when "RunPlugin" was called.调用“RunPlugin”时,该参数将被忽略。

This would remove any branching on types for your interfaces and you'd only have to create this one class - it could wrap any instance of the no-parameter interface.这将删除您的接口类型的任何分支,您只需要创建这一类 - 它可以包装无参数接口的任何实例。 So there shouldn't be any special code needed for each plugin.所以每个插件都不应该有任何特殊的代码。 Just create the wrapper when you create the instance of the plugin.只需在创建插件实例时创建包装器即可。

(By the way, this is the Adapter pattern .) (顺便说一下,这是适配器模式。)

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

相关问题 如何通过只有一个类型而不使用反射来访问一个类型实现的 static 抽象接口成员? - How to access a type's implemented static abstract interface members by having only a type without using reflection? 使用Reflection获取未由接口实现的对象的所有属性 - Using Reflection to get all properties of an object not implemented by an interface 如何使用反射确定已实现接口的通用类型? - How do I determine the generic type of an implemented interface using reflection? 继承VB中的接口-从C#移植代码 - Inherits Interface in VB - Porting code from C# 使用反射查找已实现的接口 - Using reflection to find interfaces implemented 反射:使用typeof()或IsAssignableFrom()检查类实现的接口时的行为不同 - Reflection : Different behavior while using typeof() or IsAssignableFrom() to check interface implemented by a class 尽管没有实现接口,代码工作仍未实现 - Code work despite the interface is not implemented 反思说接口方法在实现类型中是虚拟的,不是吗? - Reflection says that interface method are virtual in the implemented type, when they aren't? 将C#反射代码移植到Metro-Ui - Porting C# reflection code to Metro-Ui 使用非托管dll从VBA到C#的唯一移植代码 - Unique porting code from VBA to C# using an unmanaged dll
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM