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

时间:2011-08-17 13:39:20

标签: c# generics .net-3.5

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

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<>

2 个答案:

答案 0 :(得分:3)

假设您正在使用.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,使{{1}}非通用吗?根据您希望如何使用它,这可能是也可能不可行 - 这两个选项都有意义。

答案 1 :(得分:2)

没有这样的事情。开放式泛型不能静态编译/链接。它有一个真实的类型(typeof(IHandle<>)),但你只能使用反射来操作它。

您可以做的最好的事情是将object(或{4}}存储在.NET 4中),或者像您一样存储为公共基本类型dynamic,但要么方式,您将无法编译IHandle事件的编译时间。您必须使用Reflection或强制转换才能在运行时调用它。

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

Handle

只要将有效的通用public void RegisterHandler(IHandle<TMessage> handler) { ... } 作为参数传入,就可以在没有泛型参数的情况下编写方法调用。您可以使用它来限制进入非通用IHandle<TMessage>对象集合的内容,然后使用Reflection方法(特别是IHandleGetInterfacesGetMethod方法)来实际上调用处理程序。或者,如您的示例所示,如果您已经知道事件类型,那么只需尝试演员。