为什么我们需要在 C# 中进行向上转型和向下转型?

Why do we need Upcasting & Downcasting in C#?

考虑我们有三个 classes,Shape 是基础 class 和另外两个 classes Circle 和 Text 从基础 classe 继承(形状)

Shape.cs

namespace ObjectOriented
{
    public class Shape
    {
        public int Height { get; set; }
        public int Width { get; set; }
        public int X { get; set; }
        public int Y { get; set; }

        public void Draw()
        {

        }
    }
}

Text.cs

using System;

namespace ObjectOriented
{
    public class Text : Shape
    {
        public string FontType { get; set; }
        public int FontSize { get; set; }

        public void Weirdo()
        {
            Console.WriteLine("Weird stuff");
        }

    }
}

Circle.cs

namespace ObjectOriented
{
    public class Circle : Shape
    {

    }
}

我们都知道向上转型总是成功的,并且我们从子class引用

创建对基class的引用
Text txt = new Text();
Shape shape = txt //upcast

向下转换可能会抛出 InvalidCastException 例如

Text txt = new Text();
Shape shape = txt; //upcast
Circle c = (Circle)shape; //DownCast InvalidCastException

我感到困惑的是,我们可能需要 up/down 转换和对象的 situations/cases 是什么?

通常,您对存储在列表中的对象使用向上转换。 (共享基类型,甚至可以使用Object

List<Shape> shapes = new List<Shape>();

shapes.Add((Shape)new Text());    // <-- the upcast is done automatically
shapes.Add(new Circle());

向下转型时,需要检查类型是否正确:

foreach(Shape shape in shapes)
{
    if(shape is Circle)
    {
        Circle circle = (Circle)shape;
        // do something..
    }

}

一件事是,CircleText 都是 Shape,但是您不能将 Circle 转换为 Text。这是因为两个 classes 都扩展了 Shape class 并添加了不同的 functionality/properties。

例如:ACar和ABike共享同一个基地Vihicle(用于运送人或货物的东西,尤指在陆地上),但是 Bike 用鞍座扩展了 Vihicle,而 Car 用例如电机扩展了车辆。因此它们不能互相 'casted' ,但两者都可以看作是 Vihicle.


有一些有用的扩展方法,可以处理类型检查:

foreach(Circle circle in shapes.OfType<Circle>())
{
    // only shapes of type Circle are iterated.
}

'Real world' 示例:

如果你有一个Window,上面有很多控件。喜欢 Labels/Buttons/ListViews/etc。所有这些控件都存储在它们的基本类型的集合中。

例如 WPF 控件:所有子控件 (在 FrameWorkElement 中) 都存储在 UIElementCollection 中。所以所有添加为子 的控件必须 来自 UIElement.

当您迭代所有子控件 (UIElement's) 并搜索例如标签时,您必须检查它的类型:

foreach(UIElement element in this.Children)
{
    if(element is Label)
    {
        Label myLabel = (Label)element;
        myLabel.Content = "Hi there!";
    }
}

此循环会将所有标签 (在此范围内) 更改为 'Hi there!'。