InspiredBy
InspiredBy

Reputation: 4320

Efficient way to call a method dynamically based on its name without reflection

Let me try to simplify my question:

I have four classes: Admins, Users, Players, Roles

The database returns names of methods that I will need to execute. For example if Admins_GetName is returned then GetName() method will need to be executed on the Admins class. If Players_GetRank is returned then GetRank() method will need to be called on the Players class.

I don't want to write a huge IF or SWITCH statement with all my business logic in it. What would be the most efficient solution WITHOUT using reflection ? If possibly I would like to avoid the performance hit that reflection brings.

Keep in mind that all methods may have different parameters and but will return strings.

Here is what I'm thinking to do now: 1) Have a method with a switch statement that will break apart the database value and find the class and method I need to execute. Something like:

switch(DbValue)
{
 case DbValue == "Admins_GetName":
  Declare a delegate to Admins.GetName();
  return;
 case: DbValue = "Players_GetRank"
  Declare a delegate to Players.GetRank();
  return;
  . 
  .
  .
  etc
}

return class/method reference;

2) Pass the declaration from above to:

var myValue = Retrieved method.invoke()

Can you guys suggest me with the best way to accomplish this or help me out with the correct syntax on my idea of how to implement it.

Thank you.

Upvotes: 3

Views: 1532

Answers (1)

Marc Gravell
Marc Gravell

Reputation: 1062780

Needs a little more context; for example, do all the methods in question have the same signature? In the general case, reflection is the most appropriate tool for this, and as long as you aren't calling it in a tight loop, it will be fine.

Otherwise, the switch statement approach is reasonable, but has the maintenance overhead. If that is problematic, I would be tempted to build a delegate cache at runtime, for example:

using System;
using System.Collections.Generic;
public class Program
{
    public string Bar { get; set; }

    static void Main()
    {
        var foo = new Foo();
        FooUtils.Execute(foo, "B");
        FooUtils.Execute(foo, "D");
    }
}
static class FooUtils
{
    public static void Execute(Foo foo, string methodName)
    {
        methodCache[methodName](foo);
    }
    static readonly Dictionary<string, Action<Foo>> methodCache;
    static FooUtils()
    {
        methodCache = new Dictionary<string, Action<Foo>>();
        foreach (var method in typeof(Foo).GetMethods())
        {
            if (!method.IsStatic && method.ReturnType == typeof(void)
                && method.GetParameters().Length == 0)
            {
                methodCache.Add(method.Name, (Action<Foo>)
                    Delegate.CreateDelegate(typeof(Action<Foo>), method));
            }
        }
    }
}

public class Foo
{
    public void A() { Console.WriteLine("A"); }
    public void B() { Console.WriteLine("B"); }
    public void C() { Console.WriteLine("C"); }
    public void D() { Console.WriteLine("D"); }
    public string Ignored(int a) { return ""; }
}

That approach can be extended to multiple target types by using generics:

static class FooUtils
{
    public static void Execute<T>(T target, string methodName)
    {
        MethodCache<T>.Execute(target, methodName);
    }
    static class MethodCache<T>
    {
        public static void Execute(T target, string methodName)
        {
            methodCache[methodName](target);
        }
        static readonly Dictionary<string, Action<T>> methodCache;
        static MethodCache()
        {
            methodCache = new Dictionary<string, Action<T>>();
            foreach (var method in typeof(T).GetMethods())
            {
                if (!method.IsStatic && method.ReturnType == typeof(void)
                    && method.GetParameters().Length == 0)
                {
                    methodCache.Add(method.Name, (Action<T>)
                        Delegate.CreateDelegate(typeof(Action<T>), method));
                }
            }
        }
    }
}

Upvotes: 5

Related Questions