为什么我无法将基类引用转换为由派生类实现的变体接口?

3

我有以下的接口和类:

private interface ITest<in T1, out T2>
{
}

private class TestClass
{
}

private class TestClass<T1, T2> : TestClass, ITest<T1, T2>
{
}

有了这些,我可以做到:

    TestClass testBase = new TestClass<Parent, Child> ();

    TestClass<Parent, Child> testGeneric = (TestClass<Parent, Child>)testBase; // Works fine
    ITest<Parent, Parent> testInterface = (ITest<Parent, Parent>)testGeneric; // Works fine

但是如果我试图将两者结合起来,我就做不到:
    ITest<Parent, Parent> testInterface = (ITest<Parent, Parent>)testBase; // Fail!

为什么我不能直接在这里进行强制类型转换?这只是C#类型转换或变体接口的限制吗?

如果接口的类型参数与泛型类的类型完全匹配,它实际上是可以工作的:

    ITest<Parent, Child> testInterfaceExact = (ITest<Parent, Child>)testBase; // Works

无法复现,这里和dotnetfiddle上都可以正常工作。请提供一个[minimal, complete, and verifiable example (mcve)]并说明您正在使用的编译器版本。 - Heinzi
我期望这个能够工作,Child 是否真的是 Parent 的子类型? - Lee
1个回答

0
问题在于TestClass并不是真正的ITest<T1, T2>。它与接口无关,因此您不能仅仅写下以下代码:
(ITest<T1, T2>)new TestClass();

另一方面,非泛型的TestClass与泛型的TestClass有关,因此以下指令实际上是一个向下转换:

(TestClass<T1, T2>)new TestClass();  // will fail at run time...

在运行时,向下转型可能成功也可能失败,这取决于实际对象是否为派生类型。编译总是会成功的。

现在,如果你想要一个基类引用指向派生对象,那是可以的。但是,如果你希望将其转换为仅派生类知道的任何东西,比如接口,你首先需要执行向下转型:

TestClass derived = new TestClass<T1, T2>();
ITest<T1, T2> interf = (TestClass<T1, T2>)derived;

你甚至不需要将其转换为接口,因为泛型类已经实现了该接口。

随着变体类型参数的增加,问题变得更加严重。如果编译器无法访问泛型参数的变异信息,它就无法检查转换是否可能。一旦你将其转换为泛型类,编译器将能够正确推断变异并分配引用:

TestClass derived = new TestClass<Parent, Child>();
ITest<Parent, Parent> interf = (TestClass<Parent, Child>)derived;

你的最后一行代码能够正常工作,这是事实:

ITest<Parent, Child> testInterfaceExact = (ITest<Parent, Child>)testBase;

这只是因为您尚未更改通用类型,因此编译器在此行没有推断出变化信息(它没有)。


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