Home > Blockchain >  How to remove switch to convert string to generic function
How to remove switch to convert string to generic function

Time:08-07

This is probably a newbie question but I couldn´t find a good answer even googling and reading a lot about the subject.

I have a generic method, lets call it Execute()

My code receives a message1:

 {
   serviceProvider: "Sever1",
   serviceType: "query",
   service: "authors"
 }

and message2 could be:

 {
   serviceProvider: "Sever2",
   serviceType: "query",
   service: "bookTitles"
}

Now, I have

public void ProcessMessage(Message message)
{
   switch (message.service)
   {
      case "authors":
        Execute<Authors>();
        break;
      case "booTitle":
        Execute<BookTitle>();
        break;
   }
}

I really don´t like this ProcessMessage method, as it does not look DRY at all. And if we add more types of services, then we need to keep adding to the switch.

How should I implement this thing? Do I need reflection? I´d rather not use reflection as the ProcessMessage can be called thousands of times and reflection would probably add a lot of overhead.

CodePudding user response:

If you want to have a dynamic switch you don't need to resort to reflection, but you'd need to register the services you want to use.

Based on your question, here's what that might look like:

void Main()
{
    this.Register("authors", () => Execute<Authors>());
    this.Register("bookTitles", () => Execute<BookTitle>());
}

private Dictionary<string, Action> _registry = new Dictionary<string, Action>();

private void Register(string key, Action action)
{
    _registry[key] = action;
}

public void ProcessMessage(Message message)
{
    if (_registry.ContainsKey(message.service))
    {
        _registry[message.service]();
    }
}

You can add as many actions as you need. And you can do that at run-time.

CodePudding user response:

My answer will be split into Two sections:

Section 1: Tell you what you really need!

I think Services dont add so often, so you shouldn't optimize the Maintenence aspect of this code,
just add a strong ASSERT if a service call is not recognized,
so while testing, you know you forgot to add it to this function!

Section 2: give you what you asked for!

I think you can use reflection, but to compansate for performance,
you should cache the Function calls in a dictionary.

 // psuedo C#

auto m_ServiceCache = new ConcurrentDictionary<string, SERVICE>()

public void ProcessMessage(Message message)
{
  Action svcFunction;
  if(!m_serviceCache.TryGetValue(message.service, out svcFunction))
  { // not found in cache
     // create the service specific instanciation, and save it to cache!
    // Take into account that the "message.service" needs to be a valid type name with namespace!
     // Also note you should Sanitize the input as it is a very clear vaulnerablity to exploit
     // totally abstract psuedo here

     getGeneralType = typeof(SERVICE);
     specificType = getGeneralType.CreateSpecificType(T=message.service)
     specificObject = specificType.Instanciate();
     m_serviceCache.Add(message.service, svcFunction);
     svcFunction = specificObject;
  }
  svcFunction(); // call the cached service.
}

cheers

  • Related