【问题标题】:Is it possible to use an open generic as constructor argument?是否可以使用开放泛型作为构造函数参数?
【发布时间】:2011-10-28 23:01:15
【问题描述】:

我创建了一个非常简单的事件发布器,它看起来像这样。

public class EventPublisher
{
    private readonly IList<Func<IHandle>> _subscribers;

    public EventPublisher(IList<Func<IHandle>> subscribers)
    {
        _subscribers = subscribers;
    }

    public void Publish<TPayload>(TPayload payload)
        where TPayload : class
    {
        var payloadHandlers = _subscribers.OfType<Func<IHandle<TPayload>>>();

        foreach (var payloadHandler in payloadHandlers)
        {
            payloadHandler().Handle(payload);
        }
    }
}

这是我必须发布的消息。

var subscribers = new List<Func<IHandle>> {() => new SomeHandler()};
var eventPublisher = new EventPublisher(subscribers);

eventPublisher.Publish(new SomeMessage { Text = "Some random text..." });

我遇到的问题是发布消息时找不到任何可以处理有效负载的处理程序。这是有道理的,因为我将订阅者注册为 Func&lt;IHandle&gt; 而不是 Func&lt;IHandle&lt;T&gt;&gt;

我的处理程序类继承的接口来自 Caliburn.Micro.EventAggregator,它看起来像这样。

public interface IHandle {}

public interface IHandle<TMessage> : IHandle {
    void Handle(TMessage message);
}

_subscribers 的类型必须是什么才能处理泛型类型可以是任何具体类型的IHandle&lt;&gt;

【问题讨论】:

  • 如果您仍在使用 Windsor,您可能会受益于使用它来实例化处理程序。它内置了对此类场景的支持。

标签: c# generics .net-3.5


【解决方案1】:

假设您使用的是 .NET 4,我希望这可以工作:

var subscribers = new List<Func<IHandle<SomeMessage>>> {() => new SomeHandler()};

然后得到协方差:

public EventPublisher(IEnumerable<Func<IHandle>> subscribers)
{
    _subscribers = subscribers.ToList();
}

这使您可以订阅与Func&lt;IHandle&gt; 兼容的任何事物序列。请注意,它确实显着改变了语义,因为订户集将在构造后固定。我个人认为这是一件好事,但它可能不适合你。

或者,EventPublisher 本身是否必须与多种类型一起使用?你能做到EventPublisher&lt;T&gt; 并在任何地方使用IHandle&lt;T&gt;,使Publish 非通用吗?根据您的使用方式,这可能可行,也可能不可行 - 这两个选项都有意义。

【讨论】:

  • 很遗憾,我忘了为 C# 3.5 添加标签。 .NET 3.5 是否有类似的可能? EventPublisher 仅适用于继承 IHandle&lt;&gt; 的类型。
  • @gcso:不,没有 .NET 4 你会遇到问题。 (它是 .NET 3.5,而不是 C# 3.5,顺便说一句 - .NET 3.5 附带的 C# 版本是 C# 3.0。)你可以让 EventPublisher 通用吗?那会更简单...
  • 谢谢,乔恩。这一定是我找不到 c#3.5 标签的原因。 :D
【解决方案2】:

没有这样的事情。一个开放的泛型不能被静态编译/链接。它有一个真实的类型(typeof(IHandle&lt;&gt;)),但你只能使用反射对其进行操作。

您可以做的最好的事情是将然后存储为 object(或 .NET 4 中的 dynamic),或作为常见的基本类型 IHandle,就像您所做的那样,但无论哪种方式,您都不会拥有编译时访问Handle 事件。您必须使用反射或强制转换在运行时调用它。

首先要绑定事件,您可以利用方法not构造函数)中的类型推断,即

public void RegisterHandler(IHandle<TMessage> handler) { ... }

只要您传入一个有效的泛型IHandle&lt;TMessage&gt; 作为参数,就可以在没有泛型参数的情况下编写方法调用。您可以使用它来限制非泛型IHandle 对象集合中的内容,然后使用反射方法(特别是GetInterfacesGetMethodMakeGenericMethod 方法)来实际调用处理程序。或者,如您的示例所示,如果您已经知道事件类型,则只需尝试强制转换。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多