C#泛型类中的协变性

3

C# 4.0 .NET 4.5 Silverlight 5 我发现很奇怪,找不到解决方案,所以需要帮助。

我有一个基类Base和派生类Child : Base。我还有一个帮助类,它具有用于特定工作的泛型类型,其中T:EntityObject是EF实体Helper。

Child使用特定实体MyEntity : EntityObject进行特定工作。

所以我尝试:

public class Base
{
    protected Helper<EntityObject> helper;
}
public class Child : Base
{
    public Child()
    {
        helper = new Helper<MyEntity>();
    }
}

我认为更具体的泛型参数应该由更多派生类知道,这就是协变所用之处...但是这样做并不起作用...

那么设计这种类的“正确”方式是什么?

编辑:抱歉,我没有完全说明为什么我不能达到我需要的目标。

a. 有关基本泛型的解决方案 无法正常工作,因为基类的用户不知道 T 类型。 想象一下:

public class User
{
    private Base<T> base; // this will not compile.
    public User(TypeEnum t)
    {
        if(t == TypeEnum.MyEntity) base = new Child();
...
解决方案中的接口不起作用,因为helper在所有地方都使用T(这是它的目的对吧?)。想象一下它有一个方法。
public IEnumerable<T> Process(IEnumerable<T> items) { return items; }

我该如何在不知道T的界面中呈现它?

1
你看到了什么错误?“不起作用”是什么意思? - Peter K.
2
Helper是什么样子的?MyEntityEntityObject之间有什么关系? - Matthew Watson
1
辅助类在这里是最重要的部分。 - NSGaga-mostly-inactive
@MatthewWatson 我的实体:EntityObject(请参见问题) - Boppity Bop
@PeterK 它无法编译。 - Boppity Bop
错误是什么? - Peter K.
2个回答

5
我想这就是您想要的内容:

我认为这是您想要的:

public class Base<T> where T : EntityObject
{
    protected Helper<T> helper;
}
public class Child : Base<MyEntity>
{
    public Child()
    {
        helper = new Helper<MyEntity>();
    }
}

编辑(回应你的编辑):你可以添加一个Base,用法如下:

public class Base
{
    // put anything here that doesn't rely on the type of T
    // if you need things here that would rely on T, use EntityObject and have 
    // your subclasses provide new implementations using the more specific type
}
public class Base<T> : Base where T : EntityObject
{
    protected Helper<T> helper;
}
public class Child : Base<MyEntity>
{
    public Child()
    {
        helper = new Helper<MyEntity>();
    }
}
public class User
{
    private Base myBase;
    public User(TypeEnum t)
    {
        if(t == TypeEnum.MyEntity) myBase = new Child();
        ...

@BoppityBop 我更新了我的回答,这符合你的需求吗?如果不是,我很难理解你想如何使用它。也许如果你能解释一下:你需要什么样的多态性,Base类需要哪些公共成员等等,那可能会更有意义。 - Tim S.
Tim,那确实是一个解决方案……但有个小问题——这也不会编译……必须提供通用参数……真糟糕。我认为这是泛型的自然用法……但显然(根据SO大师的说法)这是魔法……唉……我无话可说。 - Boppity Bop
@BoppityBop 天啊! 我忘记让 Base<T> : Base。现在应该可以编译了。 - Tim S.
抱歉,Tim。你得到的解决方案是我编辑中接口问题的变体。用户将无法访问泛型类型T的好处,因为它被Base屏蔽了...我该怎么使用它?没有办法...看起来这没有解决方案。 - Boppity Bop
所以您正在尝试通过在运行时使用枚举值选择 T 来让编译器知道 T?是的,那是不可能的。即使将 User 转换为 User<T> where T:EntityObject,编译器仅将 T 视为 EntityObject,因此您无法(仅使用类型安全的编译代码)利用它有时是例如 MyEntity 的事实...除非您还创建一个 MyEntityUser:User<MyEntity> 类。 - Tim S.

4
如果 FooBar,那并不意味着 Some<Foo>Some<Bar>。有两种方法可以实现所需的功能。第一种是将基础类型设置为通用类型,例如:
Base<T> where T : EntityObject {
    protected Helper<T> helper;
}
Child : Base<MyEntity> {...}

第二种方法是在基础类型上使用非通用接口,即具有:

Base {
    protected IHelper helper;
}
Child : Base {...}

在后一种情况下,Helper<T>:IHelper,其中IHelper是非泛型的并将被定义。
顺便提一下,你可能会发现通过构造函数传递值比使用protected字段更容易。

Marc。这些解决方案都不可用。因为如果Base<T>,那么Child的用户就不知道T(否则我为什么需要泛型)。在接口的情况下 - 你不能在IHelper中声明使用T的方法,而helper在每个地方都使用它们(这是它的目的)... 我更新了我的问题并进行了解释...但我相信你自己也能看到...有没有出路? - Boppity Bop
@BoppityBop 确实,这些是限制;泛型并不是一种魔法棒,可以使代码做出与通常情况下不同的事情。 - Marc Gravell

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