C# 失败的协变转换

C# Failing Covariant Cast

我正在尝试创建具有一系列实现的类似通用 table 工厂的东西。下面的示例是不言自明的,即使两个类型属性都标记为 'out',最终转换也不起作用。我想,问题是 Table 不是接口,这会破坏演员阵容。但它不应该是真正的接口,尤其不是协变的。任何想法如何正确投射?绝对不想用反射来使用方法

interface ITableRow { }

class Table<T> where T : ITableRow { }

interface ITableFactory<out TRow, out TTable>
    where TRow : ITableRow
    where TTable : Table<TRow>
{
    TTable CreateTable();
}

// example implementation:

class SuperRow : ITableRow { }

class SuperTableFactory : ITableFactory<SuperRow, Table<SuperRow>>
{
    public Table<SuperRow> CreateTable() { throw new NotImplementedException(); }
}

// run:

class VarianceTest
{
    public static void Test()
    {
        var factory = Activator.CreateInstance(Type.GetType("Snippets.Var.SuperTableFactory"));

        var casted = (ITableFactory<ITableRow, Table<ITableRow>>) factory; // cast fails
    }
}

您的 SuperTableFactory 声称可以生成 Table<SuperRow> 的实例。这意味着,无论您从中得到什么 table,您都可以添加 SuperRows,然后读取 SuperRows

然而,您的 ITableFactory<ITableRow, Table<ITableRow>> 声称它产生 Table<ITableRows> -- table 人们可以添加 ITableRows 并从中读取 ITableRows

但您的实际 table 工厂生产 Table<SuperRow>s -- 底层行存储是 SuperRow。你不能让别人把任何旧的 ITableRow 放在那里!

这就是你的转换失败的原因。

如果去掉 "people can add rows to tables" 位就可以解决这个问题,并保证人们只能读取行。那么 Table 的行存储是否实际上是 SuperRow 并不重要,因为人们不能尝试将其他类型的行放在那里。

你通过使 Table<T> 协变来做到这一点。但是正如您所指出的, 类 不能是协变的,只能是接口。所以你需要一个 ITable<out T>.