Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can't convert concrete type to generic version of its Interface in C#

Tags:

c#

generics

I have the following interface:

public interface INotificationHandler<T>
{
    Task<T> Handle(string msg);
}

And several classes that happily implement it like so:

public class FooHandler : INotificationHandler<Foo>
{
    public Task<Foo> Handle(string msg) { return Task.FromResult<Foo>(new Foo()); }
}

public class BarHandler : INotificationHandler<Bar>
{
    public Task<Bar> Handle(string msg) { return Task.FromResult<Bar>(new Bar()); }
}

I'd like to keep a collection of INotificationHandler instances in a collection and when I get a message "foo", use the FooHandler, "bar" gets the BarHandler, etc...

var notificationHandlers = new Dictionary<string, INotificationHandler<object>>();
notificationHandlers["foo"] = new FooHandler();
notificationHandlers["bar"] = new BarHandler();
...
public void MessageReceived(string type, string msg)
{
    INotificationHandler<object> handler = notificationHandlers[type];
    handler.Notify(msg).ContinueWith((result) => /* do stuff with a plain object */)
}

However this fails to compile because my generic has no common base type, which is by design. Any object should be able to be returned from a INotificationHandler in MessageReceived.

Cannot implicitly convert type FooHandler to INotificationHandler<object>. An explicit conversion exists (are you missing a cast?)

How can I work with INotificationHandler<T> so that I don't need care about the generic types of its concrete implementations?

like image 340
plemarquand Avatar asked Aug 06 '15 14:08

plemarquand


1 Answers

If you need a type safety you can use the following hierarchy.

public interface INotificationHandler
{
    Task<object> Handle(string msg);
}

public abstract BaseHandler<T> : INotificationHandler
{
    Task<object> INotificationHandler.Handle(string msg)
    {
        return Handle(msg);
    }

    public abstract Task<T> Handle(string msg);
}

public class FooHandler : BaseHandler<Foo>
{
    public override Task<Foo> Handle(string msg) { return Task.FromResult<Foo>(new Foo()); }
}

public class BarHandler : BaseHandler<Bar>
{
    public override Task<Bar> Handle(string msg) { return Task.FromResult<Bar>(new Bar()); }
}

var notificationHandlers = new Dictionary<string, INotificationHandler>();
notificationHandlers["foo"] = new FooHandler();
notificationHandlers["bar"] = new BarHandler();
...
public void MessageReceived(string type, string msg)
{
    INotificationHandler handler = notificationHandlers[type];
    handler.Notify(msg).ContinueWith((result) => /* do stuff with a plain object */)
}
like image 194
Viacheslav Smityukh Avatar answered Oct 23 '22 22:10

Viacheslav Smityukh