【问题标题】:Is it possible to keep a list of open generic types in one place?是否可以在一个地方保留一个开放泛型类型的列表?
【发布时间】:2014-07-05 02:23:48
【问题描述】:

我正在尝试拥有一个 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

【问题讨论】:

  • 我会将消息类型和消息处理程序之间的关系保留在配置文件(app.config 或 web.config)和用户依赖注入或 IOC 中,以便在收到消息时实例化和“处理”。 unity.codeplex.com
  • IDictionary&lt;Type, object&gt; 听起来可以吗?
  • 难道你不能让IMessageHandler 公开一个Type 属性来告诉你它处理什么而不是让它通用吗?
  • 你可能想看看 MVVMLight。它实现了与其信使类非常相似的东西。 [1]:mvvmlight.net/doc
  • 您的 Handle 方法是通用的,但它并没有真正为您带来任何好处,因为它们只在运行时使用。

标签: c# generics open-generics


【解决方案1】:

每个人都知道扩展方法。但是“扩展字段”呢?当然不可能用一些新字段来扩展一些对象,但是......你见过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);
    }
}

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

【讨论】:

    【解决方案2】:

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

    所以:

    interface IMessageHandler<T> : IMessageHandler
    

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

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

    【讨论】:

      【解决方案3】:

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

      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&lt;Type, object&gt;,直接存储IMessageHandler&lt;TMessage&gt;实例。

      更新 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);
          }
      }
      

      【讨论】:

        【解决方案4】:

        不是基于你展示的内容。 IMessageHandler&lt;byte[]&gt;IMessageHandler&lt;string&gt; 是不同的类型,没有可用于“通用”列表类型的基本类型(object 除外)。

        一些选项:

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

        如果您只支持一小组处理程序(您只提到两个),则使用泛型方法和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));
           }
        }
        

        【讨论】:

          【解决方案5】:

          根据更全面的要求,另一种选择可能是让您的 IMessageHandlerType 公开为属性,所以...

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-06-01
            • 2011-01-16
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多