我最近必须帮助解决某人从通用方法返回时遇到的问题,尽管有多个问题需要解决,但我理解并能解释所有问题 - 除了......
我最近不得不帮助某人解决从通用方法返回时遇到的问题,虽然有多个问题需要解决,但我理解并能解释所有问题 - 除了让编译器接受返回类型的最后一个障碍。虽然我最终成功让程序编译并正确运行,但我仍然无法完全理解 为什么 必须这样做的逻辑。
我在下面用一个最小的例子重现了这个问题。给定一个非常简单的父子类结构,只有一个泛型方法:
abstract class AbstractParentClass
{
public abstract T DoThing<T>() where T : AbstractParentClass;
}
class ConcreteChildClass : AbstractParentClass
{
public override T DoThing<T>()
{
return this;
}
}
这会导致返回行出现错误 Cannot implicitly convert type 'ConcreteChildClass' to 'T'
。有点奇怪,因为 T
被限制为的实例 AbstractParentClass
,并且 this
显然是其中之一,但好吧,当然,所以我们会明确地这样做:
public override T DoThing<T>()
{
return (T) this;
}
现在错误消息显示为 Cannot convert type 'ConcreteChildClass' to 'T'
。什么?如果 T
不受约束,那么当然,我知道我们无法保证这一点,但是对于像我们这样的继承,它肯定应该是一个简单的转换?
我们可以通过明确转换为父类来接近:
public override T DoThing<T>()
{
return (AbstractParentClass) this;
}
现在错误显示为 Cannot implicitly convert type 'AbstractParentClass' to 'T'. An explicit conversion exists (are you missing a cast?)
。为什么我们不能隐式转换为约束的确切类型——什么可能的情况会导致约束类型的类不能转换为......它自己?但至少现在这是可以解决的,错误消息甚至直接告诉我们如何做到这一点:
public override T DoThing<T>()
{
return (T)(AbstractParentClass) this;
}
现在一切都运行正常。如果我们愿意,我们甚至可以让它看起来更漂亮一点:
public override T DoThing<T>()
{
return this as T;
}
纵观这一切,如果 T
不受约束,我当然明白为什么这些转换不可能像书面上写的那样实现。但事实确实如此,而且编译器不应该有任何问题。编译器是否出于某种原因,在允许隐式转换时不考虑类型约束?根据我的经验,C# 中所有这样的情况背后都有很好的理由,我只是一辈子也想不出这个。
如果有人知道为什么编译器会对这段(看似!)非常简单的代码产生麻烦,我将非常感谢您解释允许这些转换的问题。
因为从技术上来说你可以这样做:
class ConcreteChildClass2 : AbstractParentClass
{
public override T DoThing<T>()
{
return null;
}
}
var ccc = new ConcreteChildClass();
var ccc2 = ccc.DoThing<ConcreteChildClass2>();
即使你进行了强制转换,这也会在运行时崩溃:
System.InvalidCastException: Unable to cast object of type 'ConcreteChildClass' to type 'ConcreteChildClass2'.
如果您这样做 return this as T
,那么您将获得 null
结果而不是强制转换异常。
换句话说,约束并不保证 DoThing
返回与定义类相同的类型,它只保证返回继承自 AbstractParentClass
.