下转型和上转型

104

我刚开始学习 C#(以及 OOP)。当我有以下类似代码时:

class Employee
{
    // some code
}


class Manager : Employee
{
    //some code
}

问题 1:如果我有其他代码执行以下操作:

   Manager mgr = new Manager();
   Employee emp = (Employee)mgr;

这里的Employee是一个Manager,但是当我将其强制转换为Employee时,这意味着我在进行向上转换?

问题2:

当我有多个Employee类对象,并且其中一些但不是全部是Manager, 如何在可能的情况下将它们向下转换?


9
向上转型可以不需要显式地进行类型转换。因此,Employee emp = mgr; 就足够了。 - kiss my armpit
6个回答

111
  1. 没错。当你这样做时,你将它转换为一个employee对象,这意味着你无法访问任何特定于管理器的内容。

  2. 向下转型是将一个基类对象尝试转换为更具体的类。可以使用is和显式转换来实现:

    if (employee is Manager)
    {
        Manager m = (Manager)employee;
        //do something with it
    }
    

或者使用as运算符,像这样:

Manager m = (employee as Manager);
if (m != null)
{
    //do something with it
}

如果有任何不清楚的地方,我很乐意进行更正!


我需要一个例子来了解什么是向下转型? - user184805
5
避免重新定义已经确立的术语:在面向对象编程和C#语言中,“boxing”表示将值类型对象包装成引用类型对象,意义相当不同。此外,您的示例应该使用as运算符而不是is运算符,然后跟随一个转换操作。 - Konrad Rudolph
3
我在第一个观点上认错了,我更改了我的答案的后半部分以展示两种方法。 - RCIX
4
你的第一个陈述(“将Manager类的实例强制转换为‘employee’对象[...]意味着你无法访问任何特定于manager的内容”)并不完全准确。在 OP 的示例中,如果Employee有一个虚成员被Manager重写,CLR将调用Manager的实现,尽管进行了类型转换。从C#多态性的MSDN文章中可以看到:“当派生类覆盖虚成员时,即使以基类实例的形式访问该类的实例,也会调用该成员。” MSDN提供的示例几乎是相同的。 - Antony

52

向上转型(使用(Employee)someInstance)通常很容易,因为编译器可以在编译时告诉您一个类型是否派生自另一个类型。

向下转型通常必须在运行时执行,因为编译器并不总是知道实例是否属于给定的类型。C# 提供了两个运算符用于此操作 - is 可以告诉您向下转换是否成功,并返回 true/false。而 as 则尝试进行强制转换,如果可能则返回正确的类型,否则返回 null。

要测试员工是否为经理:

Employee m = new Manager();
Employee e = new Employee();

if(m is Manager) Console.WriteLine("m is a manager");
if(e is Manager) Console.WriteLine("e is a manager");

你也可以使用这个

Employee someEmployee = e  as Manager;
    if(someEmployee  != null) Console.WriteLine("someEmployee (e) is a manager");

Employee someEmployee = m  as Manager;
    if(someEmployee  != null) Console.WriteLine("someEmployee (m) is a manager");

Employee m = new Manager(); 这不是多态吗?(我现在还在学习面向对象编程,遇到了向上转型/向下转型和多态)我觉得它们非常相似,有什么指导意见吗? - paraJdox1
是的,没错。它们在这方面是不同的。多态性是一个可以互换使用的通用术语和一种能力,而向上转型和向下转型则是以某种方式引用它的行为。形容词与动词。 - Preet Sangha

23
  • 向上转型是将子类引用转换为基类引用的操作。 (子类->超类) (例如:Manager -> Employee)
  • 向下转型是将基类引用转换为子类引用的操作。 (超类->子类) (例如:Employee -> Manager)

在你的情况下

Employee emp = (Employee)mgr; //mgr is Manager

你正在进行向上转型。

向上转型总是成功的,不像向下转型需要显式转换,因为它在运行时可能会失败。(InvalidCastException)。

C#提供了两个运算符来避免抛出此异常:

从以下开始:

Employee e = new Employee();

第一:

Manager m = e as Manager; // if downcast fails m is null; no exception thrown

其次:

if (e is Manager){...} // the predicate is false if the downcast is not possible 

警告:当您进行向上转型时,您只能访问超类的方法、属性等内容...


7

如果您需要检查每个Employee对象是否为Manager对象,请使用OfType方法:

List<Employee> employees = new List<Employee>();

//Code to add some Employee or Manager objects..

var onlyManagers = employees.OfType<Manager>();

foreach (Manager m in onlyManagers) {
  // Do Manager specific thing..
}

3

答案1: 是的,这被称为向上转型,但你所做的方式并不是现代的方法。向上转型可以隐式执行,不需要进行任何转换。因此,只需编写Employee emp = mgr;即可实现向上转型。

答案2: 如果创建Manager类的对象,我们可以说Manager是一名员工。因为class Manager : Employee描述了员工类和经理类之间的Is-A关系。所以我们可以说每个经理都是一名员工。

但是,如果我们创建Employee类的对象,我们不能说这个员工是经理,因为class Employee是一个没有继承任何其他类的类。因此,你不能直接将该Employee类对象向下转型为Manager类对象。

因此,答案是,如果你想从Employee类对象向下转型为Manager类对象,首先必须拥有Manager类的对象,然后可以进行向上转型,然后再进行向下转型。


0

向上转型和向下转型:

向上转型:从派生类向基类进行转换 向下转型:从基类向派生类进行转换

让我们通过一个例子来理解:

考虑两个类Shape作为我的父类和Circle作为派生类,定义如下:

class Shape
{
    public int Width { get; set; }
    public int Height { get; set; }
}

class Circle : Shape
{
    public int Radius { get; set; }
    public bool FillColor { get; set; }
}

向上转型:

Shape s = new Shape();

Circle c= s;

c和s都引用同一内存位置,但它们有不同的视图。使用“c”引用,您可以访问基类和派生类的所有属性,但是使用“s”引用,您只能访问父类的属性。

向上转型的一个实际例子是Stream类,它是.net框架中所有类型的流读取器的基类:

StreamReader reader = new StreamReader(new FileStreamReader());

这里,FileStreamReader()被向上转型为streadm reder。

向下转型:

Shape s = new Circle(); 如上所述,s的视图仅为父级,为了使其同时为父级和子级,我们需要将其向下转型

var c = (Circle) s;

向下转型的实际例子是WPF的button类。


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