Reusable non generic method for generic methods

回眸只為那壹抹淺笑 提交于 2019-12-09 20:27:43

问题


I have the following base interface

public interface IHandler{
  void Handle(IMessage message);
}

and an generic interface inheriting the base interface

public interface IHandler<TMessage> : IHandler where TMessage : IMessage{
  void Handle(TMessage message);
}

My classes can implement the interface IHandler<TMessage> mutiple times. IMessage is an base interface for messages and isn´t relevant here. Currently i´m implementing the interfaces as follows.

public class ExampleHandler : IHandler<ExampleMessage>, IHandler<OtherExampleMessag>{

  void IHandler.Handle(IMessage message){
    ExampleMessage example = message as ExampleMessage;

    if (example != null) {
      Handle(example);
    }
    else {
      OtherExampleMessage otherExample = message as OtherExampleMessage;

      if (otherExample != null) {
        Handle(otherExample);
      }
  }

  public void Handle(ExampleMessage) {
   //handle message;
  }

  public void Handle(OtherExampleMessage) {
   //handle message;
  }
}

What bothers me is the way i have to implement the Handle(IMessage) method, cause in my opinion its many redundant code, and i have to extend the method each time when i implement a new IHandler<TMessage> interface on my class.

What i´m looking for is a more generic way to implement the Handle(IMessage) method (maybe in a base class for Handlers), but i´m currently stuck how to do that.


回答1:


You can use the new dynamic keyword to move the overload resolution to the DLR:

void IHandler.Handle(IMessage message)
{
    dynamic d = message;
    Handle(d);
}

Please note that this will fail at runtime with a RuntimeBinderException if the message passed in is not valid for your class.
To avoid this exception you can add a Handler for all unknown message types:

private void Handle(object unknownMessage)
{
    // Handle unknown message types here.
}

To implement IHandler.Handle in a base class, you need to do a little bit more work:

public class BaseHandler : IHandler
{
    void IHandler.Handle(IMessage message)
    {
        dynamic d = message;
        Handle(d);
    }

    private void Handle<TMessage>(TMessage message) where TMessage : IMessage
    {
        var handler = this as IHandler<TMessage>;
        if(handler == null)
            HandleUnknownMessage(message);
        else
            handler.Handle(message);
    }

    protected virtual void HandleUnknownMessage(IMessage unknownMessage)
    {
        // Handle unknown message types here.
    }
}

Your specific handler would than look like this:

public class ExampleHandler : BaseHandler,
                              IHandler<ExampleMessage>,
                              IHandler<OtherExampleMessage>
{
    public void Handle(ExampleMessage message)
    {
        // handle ExampleMessage here
    }

    public void Handle(OtherExampleMessage message)
    {
        // handle OtherExampleMessage here
    }
}

This code now works like this:

  1. The DLR calls the generic BaseHandler.Handle<TMessage> method with the real message type, i.e. TMessage will not be IMessage but the concrete message class like ExampleMessage.
  2. In this geneirc handler method, the base class tries to case itself to a handler for the specific message.
  3. If that is not successful, it calls HandleUnknownMessage to handle the unknown message type.
  4. If the cast is successful, it calls the Handle method on the specific message handler, effectifly delegating the call to the concrete Handler implementation.



回答2:


A reasonable way would be some judicious use of reflection:

var method = this.GetType().GetMethod("Handle", new[] { message.GetType() });

if (method != null) {
    method.Invoke(this, new[] { message });
}

If you are doing this so much that performance is a problem you could cache the results of the test for a massive improvement.




回答3:


You stuck because your class (in the question) does more than one thing. It deals with ExampleMessage and OtherExampleMessage. I suggest you create one class to handle one thing.

Example:

public class ExampleHandler : IHandler<ExampleMessage> 

and

public class OtherExampleHandler : IHandler<OtherExampleMessag>

From my understanding, you want to have a class to handle some kind of events. In this case, you may have to use Observer pattern to notify each Handler when something happen and let they do their work.




回答4:


The interfaces are saying that you have an instance that provides N services. Sure the services are similar but as they are for different types they are independent services. So your detecting a 'code smell'. The smell is 'why a common method for different services?'.

So are the services different enough to justify the generic interface declarations? The fundamental here is 'duplication'. Refactor out the duplication. Duplication is BAD. Once you move the duplicate stuff out then the answer will be self evident.

To put this another way, get rid of the common method and handle each in its own method ... the duplication is what you want to move out to another class. If so, think injection.

Love your smell detection!



来源:https://stackoverflow.com/questions/12855930/reusable-non-generic-method-for-generic-methods

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!