是否可以在一个地方保留一个开放的泛型类型列表?

时间:2014-05-16 15:02:17

标签: c# generics open-generics

我正在尝试使用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

5 个答案:

答案 0 :(得分:4)

每个人都知道扩展方法。但是&#34;扩展领域&#34 ;?当然,用一些新字段扩展某些对象是不可能的,但是......你见过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);
    }
}

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

答案 1 :(得分:1)

你必须让它实现另一个非通用的接口。

所以:

interface IMessageHandler<T> : IMessageHandler

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

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

答案 2 :(得分: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);
    }
}

<强>更新 当然,您可以将messageHandler的类型更改为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);
    }
}

答案 3 :(得分:0)

不基于您展示的内容。 IMessageHandler<byte[]>IMessageHandler<string>是不同的类型,并且没有基本类型(object除外)用于“常见”列表类型。

一些选项:

  • 使用Dictionary<Type, object> - 问题是,您必须使用反射或转换值。
  • 使用object数组 - 您需要进行投射,但即使您可以存储List<IMessageHandler<>>
  • ,您也需要进行投射
  • 使用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));
   }
}

答案 4 :(得分:0)

根据更全面的要求,另一个选择可能是让您的IMessageHandler公开Type作为财产......

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