当具有相似的方法代码但只是调用者名称发生变化时,使用什么方法?

What approach to use when having similar method code but just have change in the caller name?

我正在设计一个 class,它只是包装了一些属于另一个 class 库的方法调用。

这是我的 class 的样子:

public class MyClass
{
    IService Service; //Third Party Library.

    public MyClass()
    {
        // Initialization
    }

    public string MethodA()
    {
        Service.MethodA();
        return Service.GetResult();
    }

    public string MethodB()
    {
        Service.MethodB();
        return Service.GetResult();
    }

    public string MethodC()
    {
        Service.MethodC();
        return Service.GetResult();
    }

    public string MethodD()
    {
        Service.MethodD();
        return Service.GetResult();
    }
}

在反思的帮助下,我对上面的代码进行了一定程度的重构,如下所示:

public class MyClass
{
    IService Service;

    public MyClass()
    {
        // Initialization
    }

    public string MethodA()
    {
       return GetResult(System.Reflection.MethodBase.GetCurrentMethod().Name);
    }

    public string MethodB()
    {
       return GetResult(System.Reflection.MethodBase.GetCurrentMethod().Name);
    }

    public string MethodC()
    {
       return GetResult(System.Reflection.MethodBase.GetCurrentMethod().Name);
    }

    public string MethodD()
    {
       return GetResult(System.Reflection.MethodBase.GetCurrentMethod().Name);
    }

    private string GetResult(string methodName)
    {
       Service.GetType().GetMethods().FirstOrDefault(x => x.Name == methodName).Invoke(Service, null);
       return Service.GetResult();
    }
}

我看到的一个缺点是假设如果正在使用的库将来发布新版本并且方法名称有任何更改,它不会像我使用反射一样给出任何编译错误,但是在运行时,它会抛出异常。

对于这种方法,是否有更好的替代解决方案?

此外,有什么方法可以进一步优化我的代码 with/without 反射?

我会选择您提供的两个选项中的任何一个,并添加单元测试以验证用于该特定构建的第三方库版本是否仍然具有这些方法。实际上,您可以使用一些反射并深入研究这些单元测试,例如,检查方法签名是否相同等等。这些单元测试将向您保证,如果第三方库中的这些方法发生更改,您的构建将失败。

一旦构建通过(及其所有单元测试),就无需担心剩余的生命周期。

P.S。一般来说,反射很慢。如果您选择第二个选项,您可能希望在实施前对第一个选项和第二个选项进行性能比较。有了这些单元测试意味着不需要使用第二个选项。

P.S.2。我会写评论而不是 post 但我没有足够的声誉

您可以将反射的 MethodInfo 缓存到 Dictionary<string, MethodInfo> 中,这样您就不必在每次调用 MyClass.GetResult(string methodName).

时都查找它
public class MyClass
{
   private string GetResult(string methodName)
   {
      if (!_methods.TryGetValue(methodName, out MethodInfo method))
      {
         method = typeof(IService).GetMethods().FirstOrDefault(x => x.Name == methodName);
         _methods.Add(methodName, method);
      }
      method.Invoke(Service, null);
      return Service.GetResult();
   }

   private static readonly Dictionary<string, MethodInfo> _methods = new Dictionary<string, MethodInfo>();
}

此外,您可以使用 nameof expression.

消除 MyClass 中每个 public 方法中的反射
public class MyClass
{
   public string MethodA()
   {
      return GetResult(nameof(MethodA));
   }
}

或者当然,如果您的第三方依赖项更改了方法的名称,那么您仍然会遇到一个问题,即它表现为运行时错误而不是编译时错误。因此,您可以通过将 nameofIService.

上的方法名称一起使用来解决此问题
public class MyClass
{
   public string MethodA()
   {
      return GetResult(nameof(IService.MethodA));
   }
}

现在,如果 IService 上的方法名称发生更改,您会收到编译器错误。

与您的示例相比,这在性能方面应该得到更好的优化。除了……你在这一点上得到了什么? MyClass 上的每个 public 方法仍然必须直接引用它在 IService 上的相应方法。即,MyClass.MethodA 直接引用 IService.MethodA。那么,为什么不直接调用 Service.MethodA 并为自己节省反射的复杂性和性能成本呢?

此外,您担心第三方依赖项会更改方法名称,并且会产生运行时错误而不是编译时错误,此处概述的方法应该可以解决这个问题。但是,如果您的第三方依赖项更改了方法的签名怎么办?例如,IService.MethodA() 变成 IService.MethodA(string param1)?现在你回到了第一点,运行时异常而不是编译器错误。

我知道你发布的只是一个例子,我无法仅仅根据一个例子来理解你试图做的事情的完整背景。但是,基于该示例,在我看来 MyClass 的最佳版本是 没有 反射的版本。我真的很难通过使用反射调用 IService 而不是直接调用方法来了解您获得了什么。