4

我正在尝试拥有一个List开放的泛型类型。是否有可能有类似的东西:

public class MessageProcessor
{
    private IDictionary<Type, IMessageHandler<>> _messageHandlers 
        = new Dictionary<Type, IMessageHandler<>>();

    public void AddHandler<TMessage>(IMessageHandler<TMessage> handler)
    {
        var messageType = typeof(TMessage);
        // Add to dictionary here
    }

    public void Handle<TMessage>(TMessage message)
    {
        // Call the correct handler here.
    }
}

IMessageHandler应该有一个强类型的方法:

public void Handle(TMessage message) {}

我的真实例子稍微复杂一点,所以我希望我在这里正确地简化了它。

事实是,我对每个处理程序的泛型类型不感兴趣。我只需要将它们全部放在一个地方,如果我可以将它们放在那个地方,我可以轻松找到正确的处理程序。

私有字典将以消息的类型 (TMessage) 作为键。所以我希望能够做到:

// ByteArrayHandler implements IMessageHandler<byte[]>
x.AddHandler(new ByteArrayHandler()) 
// StringHandler implements IMessageHandler<string>
x.AddHandler(new StringHandler()) 

x.Handle("Some message");
x.Handle(new byte[] { 1, 2, 3} );

并有MessageProcessor正确的调用MessageHandler

4

5 回答 5

6

每个人都知道扩展方法。但是“扩展字段”呢?当然不可能用一些新字段来扩展一些对象,但是……你见过ConditionalWeakTable类吗?使用它,我们可以将一些数据附加/关联到现有对象。很酷的功能是我们不需要从该字典中显式删除项目。对象存储为弱引用,因此当 GC 收集键时,键值对会自动删除。使用它我们可以发明这个棘手的解决方案:

public class MessageProcessor
{
    private static class MessageHandlerHolder<TMessage>
    {
        public static readonly ConditionalWeakTable<MessageProcessor, IMessageHandler<TMessage>> MessageHandlers =
            new ConditionalWeakTable<MessageProcessor, IMessageHandler<TMessage>>();
    }

    public void AddHandler<TMessage>(IMessageHandler<TMessage> handler)
    {
        MessageHandlerHolder<TMessage>.MessageHandlers.Add(this, handler);
    }

    public void Handle<TMessage>(TMessage message)
    {
        IMessageHandler<TMessage> handler;
        if (!MessageHandlerHolder<TMessage>.MessageHandlers.TryGetValue(this, out handler))
            throw new InvalidOperationException("...");
        handler.Handle(message);
    }
}

所以一切都是强静态类型的,客户端不需要显式删除处理程序以避免内存泄漏。

于 2014-05-17T07:35:55.367 回答
1

你必须让它实现另一个接口,这不是通用的。

所以:

interface IMessageHandler<T> : IMessageHandler

然后,您的 MessageProcessor 类将保留对的引用IMessageHandler

typeof据我所知,“空”泛型类型仅用于。

于 2014-05-16T15:05:46.263 回答
1

沮丧似乎是不可避免的。我对此进行了概括,以允许也添加操作:

public class MessageProcessor
{
    private readonly IDictionary<Type, Action<object>> _messageHandlers = new Dictionary<Type, Action<object>>();

    public void AddHandler<TMessage>(IMessageHandler<TMessage> handler)
    {
        AddHandler((Action<TMessage>) handler.Handle);
    }

    public void AddHandler<TMessage>(Action<TMessage> handler)
    {
        var messageType = typeof (TMessage);
        _messageHandlers.Add(messageType, msg => handler((TMessage) msg));//Note: downcast
    }

    public void Handle<TMessage>(TMessage message)
    {
        var messageType = typeof (TMessage);
        _messageHandlers[messageType](message);

        //OR (if runtime type should be used):
        _messageHandlers[message.GetType()](message);
    }
}

更新: 当然,您可以将 messageHandlers 的类型更改为Dictionary<Type, object>并直接存储IMessageHandler<TMessage>实例。

更新 2: 只有在一切都是静态的情况下才能避免向下转型:

public static class MessageProcessor
{
    private static class MessageHandlerHolder<TMessage>
    {
        public static IMessageHandler<TMessage> Handler;
    }

    public static void AddHandler<TMessage>(IMessageHandler<TMessage> handler)
    {
        MessageHandlerHolder<TMessage>.Handler = handler;
    }

    public static void Handle<TMessage>(TMessage message)
    {
        MessageHandlerHolder<TMessage>.Handler.Handle(message);
    }
}
于 2014-05-16T15:33:56.170 回答
0

不是基于你展示的。 IMessageHandler<byte[]>IMessageHandler<string>是不同的类型,并且没有object用于“通用”列表类型的基本类型(除了 )。

一些选项:

  • 使用 a Dictionary<Type, object>- 问题是,您必须使用反射或转换值。
  • 使用数组object- 你需要转换,但无论如何你都需要,即使你可以存储一个List<IMessageHandler<>>
  • 使用 a Dictionary<Type, dynamic>- 您不必强制转换,但任何错误都不会在运行时出现。您可能仍然需要反射来确定 要调用哪个处理程序。

if如果您只支持一小组处理程序(您只提到两个),则使用通用方法和语句将调用分派给正确的处理程序可能更干净(也更安全) :

public void Handle<T>(T input)
{
   if (typeof(T) == typeof(string))
   {
        stringHandler.Handle(input);
   }
   else if (typeof(T) == typeof(byte[]))
   {
        byteArrayHandler.Handle(input);
   }
   else
   {
        throw new ApplicationException(string.Format("Unsupported type: {0}",typeof(T));
   }
}
于 2014-05-16T15:05:27.997 回答
0

根据更全面的要求,另一种选择可能是IMessageHandler将 aType作为属性公开,因此...

public interface IMessageHandler
{
    Type Handles {get;}
    ...
}

然后在派生类中可能看起来像......

public class StringHandler : IMessageHandler
{
    public Type Handles { get { return typeof(string); } }
    ...     
}

然后你MessageProcessor会看起来像......

public class MessageProcessor
{
    private IDictionary<Type, IMessageHandler> _messageHandlers = 
        new Dictionary<Type, IMessageHandler>();

    public void AddHandler<TMessage>(IMessageHandler handler)
    {
        //omitted error checking etc for brevity
        _messageHandlers.Add(handler.Handles, handler);
    }

    public void Handle<TMessage>(TMessage message)
    {
        //omitted error checking etc for brevity
        var handler = _messageHandlers[typeof(TMessage)];
    }
}
于 2014-05-16T15:18:46.683 回答