Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use an open generic as constructor argument?

I created a very simple event publisher and it looks like this.

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

Here is what I have to publish messages.

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

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

The issue I'm having is that publishing a message isn't finding any handlers that can handle the payload. This makes sense because I registered my subscribers as Func<IHandle> instead of Func<IHandle<T>>.

The interface my handler classes are inheriting from is from Caliburn.Micro.EventAggregator and it looks like this.

public interface IHandle {}

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

What must the type of _subscribers be to handle the IHandle<> where the generic type can be any concrete type?

like image 996
gcso Avatar asked Aug 17 '11 13:08

gcso


1 Answers

Assuming you're using .NET 4, I'd expect this to work:

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

then to get covariance:

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

That lets you subscribe with any sequence of things compatible with Func<IHandle>. Note that it does change the semantics significantly, in that the set of subscribers will be fixed after construction. Personally I'd regard that as a good thing, but it may not suit you.

Alternatively, does the EventPublisher itself have to work with multiple types? Could you make it EventPublisher<T> and use IHandle<T> everywhere, making Publish non-generic? This may or may not be feasible based on how you want to use this - both options make sense.

like image 56
Jon Skeet Avatar answered Sep 22 '22 20:09

Jon Skeet