- android - 多次调用 OnPrimaryClipChangedListener
- android - 无法更新 RecyclerView 中的 TextView 字段
- android.database.CursorIndexOutOfBoundsException : Index 0 requested, 光标大小为 0
- android - 使用 AppCompat 时,我们是否需要明确指定其 UI 组件(Spinner、EditText)颜色
我正在尝试创建具有一系列实现的通用表工厂之类的东西。下面的示例是不言自明的,即使两个类型属性都标记为“out”,最终转换也不起作用。我想,问题是 Table 不是接口(interface),这会破坏 Actor 阵容。但它不应该是真正的接口(interface),尤其不是协变的。任何想法如何正确转换?绝对不想用反射来使用方法。
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>
的实例.这意味着,无论你从中得到什么表,你都可以添加 SuperRows
,并阅读 SuperRows
.
但是,您的 ITableFactory<ITableRow, Table<ITableRow>>
声称它产生Table<ITableRows>
-- 人们可以添加的表格 ITableRows
阅读ITableRows
来自。
但您的实际表厂生产 Table<SuperRow>
s -- 底层行存储是 SuperRow
.你不能让别人放任何旧的 ITableRow
在那里!
这就是您的类型转换失败的原因。
如果您去掉“人们可以向表中添加行”位,并 promise 人们只能读取行,您就可以解决这个问题。那么Table
是否无关紧要的行存储实际上是SuperRow
,因为人们无法尝试将其他类型的行放在那里。
您可以通过制作 Table<T>
来做到这一点协变的。但是正如您所指出的,类不能是协变的,只能是接口(interface)。所以你需要一个 ITable<out T>
.
关于C# 失败的协变转换,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58530755/
在我的设置中,我试图有一个界面 Table继承自 Map (因为它主要用作 map 的包装器)。两个类继承自 Table - 本地和全局。全局的将有一个可变的映射,而本地的将有一个只有本地条目的映射。
Rust Nomicon 有 an entire section on variance除了关于 Box 的这一小节,我或多或少地理解了这一点和 Vec在 T 上(共同)变体. Box and Vec
我是一名优秀的程序员,十分优秀!