无法在类型参数化方法中将子类隐式转换为父类

我最近不得不帮助解决某人从泛型方法返回时遇到的问题,虽然有多个问题需要解决,但我理解并可以解释所有问题 - 除了让编译器接受返回类型的最后一个障碍. 尽管我最终成功地使程序能够正确编译和运行,但我仍然无法完全理解为什么必须这样做的逻辑。

我用下面的最小示例重现了该问题。给定一个非常简单的父子类结构,只有一个泛型方法:

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


以上是无法在类型参数化方法中将子类隐式转换为父类的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>