无法在C#中将具体类型转换为其接口的通用版本

7
我有以下接口:

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

还有一些很高兴地实现它的类,如下所示:

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

我希望在一个集合中保存多个INotificationHandler实例,当我收到消息“foo”时,使用FooHandler,“bar”使用BarHandler,以此类推...

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 */)
}

然而,这个代码无法编译,因为我的泛型没有共同的基类型,这是有意设计的。任何对象都应该能够从MessageReceived中的INotificationHandler返回。

无法隐式转换类型FooHandlerINotificationHandler<object>。存在显式转换(是否缺少强制转换?)

我如何使用INotificationHandler<T>来处理它的具体实现的泛型类型,以便我不需要关心它们?

2
你尝试过声明 public interface INotificationHandler<out T> 吗? - Jerry Federspiel
1
我认为你无法使T在这个接口中具有协变性,因为Task<T>是不变的。创建一个实现INotificationHandler<object>的包装类并使用它。 - Lee
1
@juharr 在代码库中的其他地方受益于INotificationHandler是一个泛型,但有些情况下我并不关心它返回的泛型类型,只关心它返回某个对象。在这种情况下,它是用于序列化的。 - plemarquand
也许可以看一下开源IoC容器是如何处理“我需要暂时忘记类型信息以保持这些异构对象的集合,但稍后又以正确的类型返回它们”的同样问题的。 - Jerry Federspiel
2
问题在于Task<T>是一个类而不是接口。你可以通过创建一个ITask<out T>接口,一个实现它并包装一个Task<T>的类,以及一个方法(比如在Task<T>上的扩展方法),该方法接受一个Task<T>并返回一个ITask<T>来实现这一点。然而,这并不容易做到。在谷歌上搜索协变任务接口给了我这个,但我还没有尝试过。 - svinja
显示剩余12条评论
1个回答

3
如果您需要类型安全,可以使用以下层次结构。
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 */)
}

如果FooHandler和BarHandler无法从同一个BaseHandler继承,这将无法工作。我们知道对象只能从一个类继承。 - Gregor Primar
它们两个都继承自同一个BaseHandler。 - Viacheslav Smityukh
是的,在你的情况下这是真的。想象一下,你已经有了一个从 A 继承的类 FooHandler 和一个从 B 继承的类 BarHandler(假设 A 是 Windows Control 而 B 是 Web Control)。在这种情况下,你的架构不能工作,因为 FooHandler 和 BarHandler 将不被允许继承自 BaseHandler。这就是我想指出的。 - Gregor Primar
是的,你说得对,但在这个问题的情况下,这是一个好的解决方案。 - Viacheslav Smityukh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接