【问题标题】:downcast and upcast垂头丧气
【发布时间】:2010-12-04 04:43:33
【问题描述】:

我是C#(和OOP)的新手。当我有如下代码时:

class Employee
{
    // some code
}


class Manager : Employee
{
    //some code
}

问题 1:如果我有其他代码可以做到这一点:

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

这里EmployeeManager,但是当我把它转换成Employee 时,这意味着我在向上转换它?

问题 2

当我有多个 Employee 类对象并且其中一些但不是全部都是 Manager 时,我如何尽可能降低它们?

【问题讨论】:

  • 无需显式转换即可完成向上转换。所以Employee emp= mgr; 就足够了。

标签: c# oop downcast upcasting


【解决方案1】:
  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
}

如果有什么不清楚的地方,我很乐意纠正它!

【讨论】:

  • 我需要例子来了解什么是向下转换?
  • 避免重新定义公认的术语:在 OOP 和 C# 的上下文中,“装箱”意味着完全不同的东西(= 将值类型对象包装到引用中)。此外,您的示例可以(并且应该)使用as 运算符而不是is,然后是演员。
  • 我在第一点上得到了纠正,我改变了答案的后半部分以显示两种方法。
  • 您的第一条语句(“...将 [Manager 类的实例] 转换为“employee”对象 [..] 意味着您无法访问任何特定于经理的内容”)并不完全准确。在 OP 的示例中,如果 Employee 有一个在 Manager 中被覆盖的虚拟成员,CLR 将调用 Manager 实现,尽管有强制转换。来自 MSDN 关于 C# 中的多态性的文章:“当派生类覆盖虚拟成员时,即使该类的实例作为基类的实例被访问,也会调用该成员。” MSDN 提供的示例几乎完全相同。
【解决方案2】:

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

向下转换通常必须在运行时完成,因为编译器可能并不总是知道所讨论的实例是否属于给定的类型。 C# 为此提供了两个运算符 - is 告诉您向下转换是否有效,并返回真/假。而 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");

【讨论】:

  • 不是员工 m = new Manager();多态性? (我现在还在学习 OOP,我遇到了向上转换/向下转换和多态)我发现它们非常相似,有什么指针吗?
  • 是的。他们在这方面是不同的。多态性是可以互换使用的事物的通用术语,并且是一种能力,而向上和向下转换则是以一种或另一种方式引用它。形容词与动词。
【解决方案3】:
  • Upcasting 是一种从子类引用创建基类引用的操作。 (子类 -> 超类)(即经理 -> 员工)
  • 向下转换 是一种从基类引用创建子类引用的操作。 (超类 -> 子类)(即员工 -> 经理)

你的情况

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 

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

【讨论】:

    【解决方案4】:

    如果您需要检查每个 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..
    }
    

    【讨论】:

      【解决方案5】:

      答案 1: 是的,它称为向上转型,但你这样做的方式不是现代方式。向上转换可以隐式执行,您不需要任何转换。所以只写 Employee emp = mgr; 向上转型就足够了。

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

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

      所以答案是,如果你想从 Employee Class 对象向下转换为 Manager Class 对象,首先你必须有 Manager Class 的对象,然后你可以向上转换它,然后你可以向下转换它。

      【讨论】:

        【解决方案6】:

        向上转换和向下转换:

        向上转换:从派生类转换为基类 向下转换:从基类转换为派生类

        我们以同样的例子来理解:

        考虑两个类Shape作为My父类,Circle作为Derived类,定义如下:

        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; }
        }
        

        向上转型:

        形状 s = new Shape();

        圆 c=s;

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

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

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

        在这里,FileStreamReader() 被向上转换为 stradm reder。

        向下转型:

        形状 s = new Circle(); 在这里,如上所述,s 的视图是唯一的父级,为了使其同时适用于父级和子级,我们需要向下转换它

        var c = (Circle) s;

        Downcasting 的实际例子是 WPF 的按钮类。

        【讨论】:

          猜你喜欢
          • 2018-10-31
          • 2019-02-13
          • 1970-01-01
          • 2021-12-25
          • 1970-01-01
          • 1970-01-01
          • 2013-07-31
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多