Search code examples
c#.net.net-standard

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


I am designing a class which is just wrapping a few methods calls which are part of another class library.

Here's what my class looks like:

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

With the help of reflection I have refactored above code to some extent as below:

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

One drawback that I am seeing is that suppose if the library which am using release new version in the future and if there are any changes in the method name, it will not give any compilation error as I am using reflection, but at runtime, it will throw an exception.

Is there any better alternative solution to this approach?

Also, is there any way I can optimize my code further with/without reflection?


Solution

  • You could cache the reflected MethodInfo into a Dictionary<string, MethodInfo> so you don't have to look it up every time you call 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>();
    }
    

    Furthermore, you could eliminate the reflection in each of the public methods in MyClass by using the nameof expression.

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

    Or course, if your third party dependency changes the name of a method then you still have a problem where that manifests itself as a runtime error instead of a compile time error. So, you could fix that by using nameof with the name of the method on IService.

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

    Now, if a method name on IService changes you get a compiler error.

    This should be better optimized in terms of performance than your example. Except... what have you gained at this point? Each public method on MyClass still has to directly reference it's corresponding method on IService. I.E., MyClass.MethodA directly references IService.MethodA. So, why not just call Service.MethodA and save yourself the complexity and performance costs of reflection?

    Furthermore, you're concerned about your third party dependency changing a method name and that creating runtime errors instead of compile time errors and the approach outlined here should address that. But, what if your third party dependency changes a method's signature? E.G., IService.MethodA() becomes IService.MethodA(string param1)? Now you're right back to square one with runtime exceptions instead of compiler errors.

    I understand what you've posted is just an example, and that I can't understand the full context of what you're trying to do based on just an example. But, based on that example, in my opinion the best version of MyClass is the version without reflection. I'm really struggling to see what you gain by using reflection to call into IService rather than just calling the methods directly.