在 C# 中编写向上转型和向下转型表达式

Writing Upcasting and Downcasting expressions in c#

最近在研究c#中的向上转型和向下转型。我知道向上转型是指从派生 class 到基础 class 的转换。然而,当我看到向上转换的实际例子(如下所示)时,我感到困惑。

public class Shape 
{
...
}

public class Circle : Shape
{
...
}

Circle circle = new Circle();
Shape shape = new Shape();
// Converting an object into its base class reference
shape = circle

如果我们将 circle 转换成它的基础 class 引用,它不应该像

circle = shape 

抱歉,如果听起来太业余了。这是因为我一直看到如下格式的表达式:

int x = 3; // means assign 3 to variable x. 

所以我很困惑为什么圆圈在右手边而不是在左手边。请指教。算我一个初学者吧。

你在你的例子中基本上做的也可以写成如下:

Circle circle = new Circle();
Shape shape = (Shape)circle; //Casting the object to the base type

所以您正在将 Circle 类型的对象转换为 Shape 类型的对象。 在您的代码中,这是 "automatically" 完成的,因为您将值分配给类型为 Shape.

的新变量

这进一步解释了 casting/base 类:https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/casting-and-type-conversions

(旁白:这被称为 upcasting 因为,传统上,绘制 class 图使得基础 classes 在物理上显示在派生的上方classes.)

现在当你这样做时:

shape = circle; // shape->(instance of Circle)

您正在将 Circle 引用分配给 Shape 引用,以便在分配后引用 shape 将引用 Circle.

这很好,因为您可以用 Shape 做的所有事情您也可以用 Circle 做。

但是,如果您这样做:

circle = shape; // circle->(instance of Shape)

您正在将 Shape 引用分配给 Circle 引用。您不能这样做,因为(如果可能的话)您将能够访问 Shape.

中不存在的 Circle 功能

例如,假设 Circle.Radius 存在但 Shape.Radius 不存在。

如果允许您将 circle 引用指向 Shape 如果您尝试访问 circle.Radius 会发生什么?答案是:会发生未定义的行为,因为 Shape.Radius 不存在。

没有

问题是:Shape 不是 Circle,但 CircleShape

这意味着 Circle 总是可以放在需要 Shape 的地方,但是当你需要 Circle 时,你不能把任何 Shape 因为它也可能是 Triangle.

这就是为什么你需要明确地转换它(使用 (Type)variablevariable as Type)。

基本上,我们假设如下:

class Shape {}
class Circle : Shape {}
class Triangle : Shape {}

现在,我们还有一个 Triangle 让我们可以更好地实际布局:

Circle c = new Circle();
Triangle t = c; // Impossible due to Circle not being a Triangle
Shape s = c; // Possible because a Circle is a Shape
Triangle t2 = s; // Impossible because Shape may be a Triangle or any other derived class

如果你了解set,你就很容易理解为什么'shape = circle'可以,'circle = shape'不可以。

想想这个。

A是Alphabet的一个字符。

所以我们可以说

public class A : Alphabet
{
}

据我们所知。

Alphabet 也是

public class Alphabet
{
}

如果我们画个图,可以是这样的

┌───────── Alphabet ───────┐
│ A B C D E ... a b c ... z│
└──────────────────────────┘

我们可以说 z 是 Alphabet,但我们不能说 Alplabet 是 z,因为 Alphabet 包含的不仅仅是 z。

所以,让我们看看圆形和形状。

Circle包含了Shape的信息,并且增加了更多的变量。 (可能不是,但可以更改。)

所以我们可以说Circle是一组Shape。

我们可以修改基于Shape的Circle。我们也可以初始化 Circle.

但是如果你这样做'Shape = Circle',Circle中的一些东西无法初始化。

它 returns 错误。

我会尽力解释:

变量shape包含对类型Shape对象的引用

当你写 Shape shape = new Shape(); 变量 shape 将包含对新 Shape 对象的引用。

当您通过写入 shape = circle 重新分配 shape 时,它包含对另一个对象的引用。对象circle的类型是circle,但是由于circle继承了shape所以做这个赋值是可以的;对 circle 进行了隐式转换以键入 Shape.

进行此转换的另一种可能更清晰的方法是进行显式转换:

Circle circle = new Circle();
Shape circleAsShape = (Shape)circle;

向上转型意味着将对象的类型更改为派生较少的基础 class(Circle 对象到 Sape)。

向下转换在另一个方向起作用,例如。从 Shape 转换为 Circle.

派生类型多的变量可以很容易地分配给派生类型少的变量,因为这里隐式地进行了向上转换,因为 Circle 也是一个 Shape。这就是为什么派生类型多的值可以分配给派生类型少的变量。

反之则行不通,因为 Shape 是通用的,我们不知道它是否是 Circle。因此,当您尝试向下转换时,您需要明确指定它:

// this won't work
Shape shape = new Shape();
Circle c = (Circle)shape;
// this will
Shape shape = new Circle();
// we know we have Circle object so we can downcast
Circle c = (Circle)shape;