是否可以使用开放泛型作为构造函数参数

本文关键字:构造函数 参数 泛型 可以使 是否 | 更新日期: 2023-09-27 18:05:57

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

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<IHandle>而不是Func<IHandle<T>>

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

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

在一般类型可以是任何具体类型的情况下,_subscribers必须是什么类型才能处理IHandle<> ?

是否可以使用开放泛型作为构造函数参数

假设您使用的是。net 4,我希望这能工作:

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

则得到协方差:

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

允许您订阅与Func<IHandle>兼容的任何内容序列。请注意,它确实显著地改变了语义,因为订阅者集将在构造之后固定。我个人认为这是一件好事,但它可能不适合你。

或者,EventPublisher本身是否必须与多种类型一起工作?你能不能把它改成EventPublisher<T>,然后到处用IHandle<T>,让Publish变成非通用的?这可能是可行的,也可能是不可行的,这取决于你想如何使用它——这两个选项都是有意义的。

没有这种事。打开的泛型不能静态编译/链接。它有一个真实的类型(typeof(IHandle<>)),但是你只能使用反射对它进行操作。

你能做的最好的是将它们存储为object(或。net 4中的dynamic),或者像你所做的那样作为一个公共基类型IHandle,但无论哪种方式,你都不能在编译时访问Handle事件。你必须在运行时使用反射或强制转换来调用它。

要首先绑定事件,您可以利用方法 (不是构造函数)中的类型推断,即
public void RegisterHandler(IHandle<TMessage> handler) { ... }
只要传入一个有效的泛型IHandle<TMessage>作为参数,就可以在没有泛型参数的情况下编写方法调用。您可以使用它来限制进入非泛型IHandle对象集合的内容,然后使用Reflection方法(特别是GetInterfacesGetMethodMakeGenericMethod方法)来实际调用处理程序。或者,如在您的示例中,如果您已经知道事件类型,那么只需尝试强制转换。