提问者:小点点

当有类似的方法代码但只是在调用方名称中有更改时,使用什么方法?


我正在设计一个类,它只是包装了一些方法调用,这些方法调用是另一个类库的一部分。

下面是我的班级的样子:

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

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

对于这种做法,有没有更好的替代解决办法?

还有,我有没有任何方法可以进一步优化我的代码,有/没有反射?


共2个答案

匿名用户

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

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

附注。 一般来说,反射是相当缓慢的。 如果选择第二个选项,则可能需要在实现前对第一个和第二个选项进行性能比较。 有了这些单元测试就意味着不需要使用第二个选项。

P.S.2。 我本想写一篇评论而不是一篇帖子,但我没有足够的声誉

匿名用户

您可以将反射的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表达式消除MyClass中每个公共方法中的反射。

public class MyClass
{
   public string MethodA()
   {
      return GetResult(nameof(MethodA));
   }
}

当然,如果您的第三方依赖项更改了方法的名称,那么您仍然存在这样一个问题,即它表现为运行时错误而不是编译时错误。 因此,您可以通过使用NameOfIService上的方法名称来修复这个问题。

public class MyClass
{
   public string MethodA()
   {
      return GetResult(nameof(IService.MethodA));
   }
}

现在,如果iservice上的方法名称发生更改,则会出现编译器错误。

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

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

我明白你所发布的只是一个例子,我无法理解你试图基于一个例子所做的全部内容。 但是,基于该示例,我认为MyClass的最佳版本是没有反射的版本。 我真的很难看到使用反射调用iservice而不是直接调用方法会得到什么。