不知道类型如何返回通用类型

8

我有一个情况,其中一个类在其泛型类型参数中接受某种对象类型的实例。布局大致如下:

public abstract BaseClass { ... }
public DiamondClass : BaseClass { ... }
public SilverClass : BaseClass { ... }

public Handler<T> where T : BaseClass { ... }

我希望能够创建一个方法来返回Handler<DiamondClass>Handler<BaseClass>的实例,而不需要在输入时定义类型。我已经尝试过类似这样的方法:

public Handler<BaseClass> GetHandler(HandlerType type)
{
    switch(type)
    {
        case HandlerType.Diamond: return new Handler<DiamondClass>();
        case HandlerType.Silver: return new Handler<SilverClass>();
        default: throw new InvalidOperationException("...");
    }
}

但是这样行不通,因为显然 Handler<DiamondClass> 不能隐式转换为 Handler<BaseClass>。我可以这样指定:
public Handler<T> GetHandler<T>(HandlerType type) where T : BaseClass
{
    switch(type)
    {
        case HandlerType.Diamond: return (Handler<T>)new Handler<DiamondClass>();
        case HandlerType.Silver: return (Handler<T>)new Handler<SilverClass>();
        default: throw new InvalidOperationException("...");
    }
}

但现在我需要调用GetHandler<DiamondClass>或者GetHandler<BaseClass>。这违背了根据枚举返回正确处理程序的方法而不知道类型的目的。我希望能够定义一个Type对象并传递它,如下所示:

 Type objType = typeof(DiamondClass);
 var handler = Handler<objType>();

但显然C#不允许这种愚蠢的做法。我已经尝试了几种不同的方式来实现它,但我被卡住了。


(实际上,我通过返回一个dynamic对象使其工作,但如果可能的话,我想避免使用它,因为它失去了任何类型安全和Intellisense支持。)


“var handler = Handler<DiamondClass>()”有什么问题,我看不出你想解决什么问题。 - CaffGeek
3
如果你在泛型方法中需要检查类型,我会一直质疑这种方法的实用性。 - Rob Allen
@RobA 请记住,为了解释起见,我大大简化了示例。问题的一部分是我希望它属于用户控件,但我不能将其定义为通用控件,因为这会破坏设计师。 - KChaloux
1个回答

10
这就是协变性发挥作用的地方,协变性和逆变性只能在接口和委托上使用,因此,要解决你的问题,只需定义一个新的接口IHandler,并将其声明为协变,使用out来指定类型参数为协变:
public interface IHandler<out T> where T : BaseClass 
{
}

具有协变类型参数的接口使其方法能够返回比类型参数指定的更派生的类型。

它可以工作。更多信息请参见此处


嘿,我不知道我们可以用 <out T> 类型来做这个!太棒了。我喜欢学习这些东西。编辑:刚试了一下,你真是救星啊。它比我想象中的简化了很多概念。我一直在想为什么它不接受 child 作为 base 的一种类型。 - KChaloux
@KChaloux:如果我理解正确,这些东西只能通过INTERFACE和DELEGATE来实现?“子类作为基类的一种类型”是什么意思? - cuongle
我正在使用对象作为类型参数,而不是基本类型。因此,在这个例子中,DiamondClassSilverClassBaseClass的子类,因此与BaseClass的类型具有“is a”关系。DiamondClass是一个BaseClass...现在我打出来听起来很混乱。 - KChaloux
@KChaloux:还有一件事,这个东西只在引用类型中起作用,而不是值类型。我仍然不明白你的问题:“为什么它不接受?” - cuongle
我基本上只是在说我不明白为什么编译器不允许从Handler<DiamondClass> => Handler<BaseClass>的隐式转换。现在我想我明白了。(我并没有在评论中提出另一个问题,只是陈述了一下)。 - KChaloux

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