通用方法的可重用非泛型方法

时间:2012-10-12 09:20:31

标签: c# generics interface

我有以下基本界面

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

和继承基本接口的通用接口

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

我的类可以多次实现接口IHandler<TMessage>IMessage是消息的基本接口,在此处不相关。目前我正在实现如下接口。

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

困扰我的是我必须实现Handle(IMessage)方法的方式,因为在我看来它有很多冗余代码,每次实现新的IHandler<TMessage>接口时我都必须扩展方法在我的课上。

我正在寻找的是一种更通用的方法来实现Handle(IMessage)方法(可能在Handlers的基类中),但我目前仍然不知道如何做到这一点。

4 个答案:

答案 0 :(得分:3)

您可以使用新的dynamic关键字将重载决策移至DLR:

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

请注意,如果传入的邮件对您的班级无效,则在运行时会因RuntimeBinderException而失败。
要避免此异常,您可以为所有未知消息类型添加处理程序:

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

要在基类中实现IHandler.Handle,您需要做更多的工作:

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.
    }
}

您的特定处理程序将如下所示:

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

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

此代码现在的工作原理如下:

  1. DLR使用真实的消息类型调用通用BaseHandler.Handle<TMessage>方法,即TMessage不是IMessage,而是具体的消息类,如ExampleMessage
  2. 在这个geneirc处理程序方法中,基类尝试将自身置于特定消息的处理程序中。
  3. 如果不成功,则会调用HandleUnknownMessage来处理未知消息类型。
  4. 如果转换成功,它会调用特定消息处理程序上的Handle方法,有效地将调用委托给具体的Handler实现。

答案 1 :(得分:1)

合理的方法是明智地使用反思:

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

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

如果您这么做以至于性能问题,您可以缓存测试结果以进行大规模改进。

答案 2 :(得分:1)

你因为你的班级(问题中)做了不止一件事而陷入困境。它涉及ExampleMessageOtherExampleMessage。我建议你创建一个类来处理一件事。

示例:

public class ExampleHandler : IHandler<ExampleMessage> 

public class OtherExampleHandler : IHandler<OtherExampleMessag>

根据我的理解,你想要一个类来处理某种事件。在这种情况下,您可能必须使用Observer pattern在发生事件时通知每个处理程序并让他们完成工作。

答案 3 :(得分:-1)

接口说你有一个提供N个服务的实例。当然,服务是相似的,但对于不同的类型,它们是独立的服务。所以你检测到“代码味道”。气味是'为什么不同服务的常用方法?'。

服务的差异是否足以证明通用接口声明的合理性?这里的根本是“重复”。重构重复。复制 BAD 。一旦你移出重复的东西,那么答案将是不言而喻的。

换句话说,摆脱常用方法并用自己的方法处理每个方法......复制就是你要移到另一个类的东西。如果是这样,请考虑注射。

喜欢你的气味检测!