C# 泛型继承:不变性妨碍调用派生 class

C# generic inheritance: invariance getting in the way of calling derived class

我想做的事情:

abstract class TileBase
{
    protected TileGroup<TileBase> tileGroup;
}
class Tile : TileBase
{
    public Tile(Province Province)
    {
        tileGroup = Province;
    }
}
abstract class TileGroup<T>
{
    protected T[] tiles;
    protected TileGroup<TileGroup<T>> tileGroup;
}
class Province : TileGroup<TileBase>
{

    public Province(Tile tile, Nation nation)
    {
        tiles = new[] { tile };
        tileGroup = nation;
    }
}
class Nation : TileGroup<Province>
{

    public Nation(Province province)
    {
        tiles = new[] { province };
        tileGroup = null;
    }
}

由于不变性,这将不起作用(如果我正确理解不变性):cannot convert Nation to TileGroup<TileGroup<TileBase>>

所以我需要这样写:

class Nation : TileGroup<TileGroup<TileBase>>
{

    public Nation(Province province)
    {
        tiles = new[] { province };
        tileGroup = null;
    }
}

但是当图层堆叠时;这很快变得丑陋:

Map : TileGroup<TileGroup<TileGroup<TileBase>>> 

这也使得在两个现有层之间添加层变得困难,因为低层中的一个更改意味着更改所有较高层。

那么我应该怎么做呢?


对不起这个公式,我知道我想要什么,但不知道我应该如何解释比这样更清楚。

为了了解发生了什么以及为什么这不起作用,您需要了解泛型类型实际上是什么。在某种程度上,它们是一种模板,可以根据语言的要求为每个有效 T 提供 精确 类型。

当你写 TileGroup<TileBase> 时,实际发生的是一个新类型,我们称之为 TileGroup_TileBase 定义如下:

class TileGroup_TileBase
{
    protected TileBase[] tiles;
    protected TileGroup<TileGroup<TileBase>> tileGroup;
}

现在,让我们继续扩展泛型类型。我们已经知道那里的 TileGroup<TileBase> 类型,但是还有一个 TileGroup<TileGroup_TileBase>,所以让我们替换它:

class TileGroup_TileBase
{
    protected TileBase[] tiles;
    protected TileGroup_TileGroup_TileBase tileGroup;
}
class TileGroup_TileGroup_TileBase
{
    protected TileGroup_TileBase[] tiles;
    protected TileGroup<TileGroup<TileGroup_TileBase>> tileGroup;
}

我们可以继续到这里,但这实际上已经足以说明问题了。让我们看看 Nation 而不是你试图在 Province 构造函数中分配的内容。 Nation 是一个 TileGroup<Province>,所以让我们展开它:

class TileGroup_Province
{
    protected Province[] tiles;
    protected TileGroup<TileGroup<Province>> tileGroup;
}

好的,我们已经足够扩展类型了。让我们来看看你正在尝试做的任务。在 Province 中,tileGroup 属性 是 TileGroup<TileGroup<TileBase>> 类型,所以基本上这就是您要尝试做的事情:

TileGroup<Province> nation = null;
TileGroup<TileGroup<TileBase>> province_tileGroup = nation;

你能看出为什么现在失败了吗?如果没有,让我们在这里使用我们扩展的泛型类型:

TileGroup_Province nation = null;
TileGroup_TileGroup_TileBase province_tileGroup = nation;

好的,这是实际使用的类型(请记住,我们这样做并不是为了理解这一点,而是那些通用类型 实际上 为每个 T 真的!)。但是如果我们看一下上面的定义,TileGroup_ProvinceTileGroup_TileGroup_TileBase 实际上并不相关。当然,它们看起来很相似,但是没有允许这种赋值的类型关系!

这就是为什么当我们在 BCL 中处理泛型类型时,我们经常有接口。因为接口允许我们在那些泛型类型具体化之间建立类型关系,然后我们可以使用这些关系将一个具体化分配给另一个。老实说,你的抽象 类 和那些受保护的字段使得用接口清理它有点困难,所以你应该考虑你是否真的需要这种类型关系以及是否有一个抽象基类型这些字段实际上是必需的。