class Program
    {
        static void Main(string[] args)
        {
            Person stu = new Student();
            stu.work();
            Console.ReadKey();
        }
    }
class Person
    {
        public virtual void work()
        {
            Console.WriteLine("我正在工作");
        }
    }
class Student : Person
    {
        public new void work()               {
        Console.WriteLine("我正在认真学习");
        }
}
问题:
1:stu是person的对象还是student的对象?stu.work();调用的到底是person的方法还是student的方法。2:把new换成override,输出结果变成“我正在认真学习”,请解释

解决方案 »

  1.   

    override就是重写父类中的方法。
      

  2.   

    一个student对象当然也就是一个person对象。stu.work调用的是stu所引用的对象的work,这要看它实例化时是什么类型,而不仅仅是所声明的变量。new这个描述符是从c++中继承来的糟粕,它是阻止了类型继承中的方法重写,它违反了面向对象设计的里氏(可替换)原则。
      

  3.   

    用overridenew是隐藏基类成员
    override是复写基类成员隐藏仅对当前类有效
    复写后 当调用此方法时 会自动调用该方法的最派生版本
      

  4.   

    Override关键字主要是提供派生类对基类方法的新实现,重写的基类方法必须和Override的方法具有相同的签名
    若参数相同,基类函数有virtual关键字。如果基类函数有多个重载版本,且派生类并没有重写所有的同名虚函数,当在派生类中调用函数时,基类中未被重写的虚函数将被隐藏
    http://www.cnblogs.com/hsapphire/archive/2009/12/16/1625365.html
      

  5.   

    new就是写出了一个方法签名相同,但是根本不继承的方法。
      

  6.   

    ,我原打算不提第二个问题,就怕大哥们潮水般回答new、与override的区别,请各位老师,先隐藏第二个问题,把第一个问题帮小弟回答一下,谢谢
      

  7.   

    1:stu是student的对象?stu.work();调用的到底是person的方法。
    解释:stu实例化时是通过new Student()实现的,所以必然是student的对象
    student中的new work导致在student中会存在两个work方法的地址,具体执行哪个就要看当前声明的类型了。
    如果是override work的话只在student中只会存在一个work方法的地址,已经将父类的work覆盖掉了,所以不管当前声明的是什么类型都会执行覆盖后的这个新的(stu已经看不到旧的了)。
      

  8.   

    谢谢,清楚一点了,不过,还想问一下:既然stu是person的对象,那么下面代码:
    class Program
      {
      static void Main(string[] args)
      {
      Person stu = new Student();
      stu.work();
      Console.ReadKey();
      }
      }
      class Person
      {
      public void A()  //把work换成A了
      {
      Console.WriteLine("我正在工作");
      }
      }
      class Student : Person
      {
      public void work()
      {
      Console.WriteLine("我正在认真学习");
      }
      }
    为什么又要报错呢?说person中没有work,既然是student的对象,就应该调student的work呀,
      

  9.   

    说错了,stu既然是student的对象.........
      

  10.   

    没有错,是student的对象当然也是person的对象了(这是最基本的继承的常识问题啊)
      

  11.   

      class Program
        {
            static void Main(string[] args)
            {
                Person stu = new GoodStudent();
                stu.work();
                ((Student)stu).work();
                ((GoodStudent)stu).work();
                Console.ReadKey();
            }
        }
        class Person
        {
            public virtual void work()
            {
                Console.WriteLine("我正在工作");
            }
        }
        class Student : Person
        {
            public new void work()
            {
                Console.WriteLine("我正在认真学习");
            }
        }
        class GoodStudent : Student
        {
            public new void work()
            {
                Console.WriteLine("我都学会了,玩呢");
            }
        }试试这段代码你就更明白了,同一个对象拥有了多个方法地址,会根据当前声明的类型选择执行
      

  12.   

    哦,那再..再问一下,Person stu = new Student();
    stu.work,说明我想执行父类的方法,那么,直接写成Person stu = new person();不就得了吗,写成Person stu = new Student();有什么实际意义呢,请问?
      

  13.   

    还有这段:
     class Program
        {
            static void Main(string[] args)
            {
                GoodStudent stu = new GoodStudent();
                ((Person)stu).work();
                ((Student)stu).work();
                ((GoodStudent)stu).work();
                Console.ReadKey();
            }
        }
        class Person
        {
            public virtual void work()
            {
                Console.WriteLine("我正在工作");
            }
        }
        class Student : Person
        {
            public new void work()
            {
                Console.WriteLine("我正在认真学习");
            }
        }
        class GoodStudent : Student
        {
            public new void work()
            {
                Console.WriteLine("我都学会了,玩呢");
            }
        }
      

  14.   


    这个问题应该是编译的问题吧
    根本无法通过编译了stu 是被定义为 Person 类型然后  stu =  new Student(); 这个时候stu的类型应该是Student()
    但是stu 也有可能 被赋予 stu = new Person()
    这个时候stu 就不能.work();
    所以为了避免这种情况(stu变量对象改变)的发生 就不允许编译了以上....是我自己的理解
      

  15.   

    答:
    1.stu的类型是Person,新建了一个Student实例;
    stu.work();这句在这里是调用了父类Person的work方法。
    2.将new改为override,调用了子类的work方法。
    可以理解为override是还用父类的方法,但是是重写父类的相同名称的方法的内容。
    new是覆盖了父类的相同名称的方法。不恰当的比方就是:
    override就是朝鲜的金老胖子,金胖子和金小胖子,还是他们金家的
    new就是中华民国 和 RPC,都是中国,性质变了,覆盖掉了
      

  16.   


    比如有2个类 继承Person 
    Student:Person 
    Teacher:Person然后有一段代码
    Person p;
    if(i == 1)
    {
    p = new Student();
    }
    else
    {
    p = new Teacher();
    }p.work();
    这个时候你是不是知道为什么要这样写了呢
      

  17.   

    执行方法的时候需要根据当前声明的类型进行选择这一点你是要切记的,当你声明为Person时,即使是student的对象也是要按照Person去选择方法的,如果不存在就报错,如果存在就要检查是否被子类override了,如果没有override就执行当前的,如果override就执行子类的
      

  18.   

    对于 stu 的类型,其实有两个
    一个是“变量”的类型(或者说申明的类型)Person
    一个是“变量引用的对象”的类型(或者说对象的实际类型)Student
    虽然实际是 Student,但编译器只认 Person
    一般直接写 Person stu = new Student(); 比较少,多在方法参数中这么用,比如:
    void WorkAWeek( Person p )
    {
      for( int i = 0; i < 5; ++ i );
        p.Work();
      p.Rest(); // 假设 Person 有个 Rest 方法显示“我在休息”
      p.Rest();
    }如果 Student 正确 override 了 Work 这个方法,那么 WorkAWeek( new Student() ); 就会显示五遍“我正在认真学习”和两遍“我在休息”另一方面,假设 Student 有个方法 Exam 而 Person 中没有,那么你用 p.Exam() 必然会报错,因为编译器只知道 p 是 Person,即便有 p = new Student() 它也是不认的。
      

  19.   


    这个图是错误的。你从哪里淘弄来的烂图?f指向的就是一个Apple实例,只不过这个变量被声明为Fruit类型,于是编译器会按照这个类型来检查你的代码使用类型接口时的一致性。说Apple实例中嵌有一个独立的Fruit实例对象,是极端错误的。一个Apple实例就是一个Apple实例,它的接口是从Fruit继承而来,就好象女人是人,有着相同的嘴巴和眼睛接口,而不是说女人身体里边还有另外一个人。
      

  20.   

    http://topic.csdn.net/u/20110212/17/DE610469-0D10-46F4-BDA5-7EE8A1AB6A1E.html#r_71586061
      

  21.   

    看图的标题,这图是用来说明类型转换的,不是用来说明变量绑定的
    至于 Apple 和 Fruit 的关系,虽然我也喜欢只有接口那种干干净净的方式,可惜目前大部分语言的继承就是把成员、实现一并继承下来的
      

  22.   

    关于那个图的说明,原话如下:Apple类继承至Fruit类,在执行Fruit f = new Apple();这句代码的时候,由于Fruit是一个引用类型,所以Fruit f会在堆栈里创建一个指针f,也就是说这个f是在堆栈里的。而new Apple的时候,它会在托管堆里面创建了一个Apple类的实例,由于Apple类继承至Fruit类,所以,在Apple类里面也包含了Fruit类的内容。接下来把Apple类的实例赋给f这个指针。这个f指向的并不是Apple类,而是Apple类里包含的Fruit类。这样做是被允许的,正是由于Apple类包含了Fruit类里的所有内容,所以这种转型才能成功。
       接下来我们来看Apple a = new Fruit();这一句。首先Apple a在堆栈里创建了一个a指针,它准备指向一个实例。接下来new Fruit()它会在托管堆里面创建一个Fruit类实例,然后想把Fruit这个实例赋给a这个指针,显然由于Fruit类并不包含Apple类里面所包含特有的内容,这个时候把它赋给一个Apple a这样做是不行的。所以呢,导致这个语句失败。
       我们可以这样去理解,打个比方,可以说苹果是一种水果,但是呢,不能说水果是一种苹果。假如CLR允许随便的转型,比如说一种类型随便转换成另外一种类型,这样呢就会违反安全性原则,将出现难以预料的结果。其中包括应用程序崩溃,以及造成安全漏洞。因为一种类型能轻松地伪装成另一种类型,类型伪装是造成许多安全漏洞的根源,并会破坏应用程序的稳定性和可靠性。因此,类型安全性是CLR一个极其重要的目标。
      

  23.   

    本来想为那幅图辩解两句,看了这段话,怎么指针都出来了呢?
    这个作者大概受C++影响比较大吧?这张图用来说明C++的继承实现比较接近,用来说明C#的继承实现就有一定距离了
      

  24.   

    using System;
    namespace ConsoleApplication5
    {
        class Program
        {
            static void Main(string[] args)
            {
               // Person stu = new Student();
                Student stu = new Student();
                student1 stu1 = new student1();
                Person p = stu; //编译时类型为person ,运行时类型为student。
                p.work();
                Person p1 = stu1; //编译时类型为person ,运行时类型为student1。
                p1.work();
                Console.ReadKey();
            }
            class Person
            {
                public virtual void work()//虚方法
                {
                    Console.WriteLine("我正在工作");
                }
            }
            class Student : Person
            {
                new public void work()//隐藏了继承而来的方法work
                {
                    Console.WriteLine("我正在学习");
                }
            }        class student1 : Person
            {
                public override void work()//重写了继承而来的方法work
                {
                    Console.WriteLine("我正在认真玩");
                }
            }
        }
    }
    结果是:
    我正在工作   //基类的方法
    我正在认真玩 //子类的方法所以当子类没有重写(override)基类的虚方法时,调用方法时,是根据该实例的编译时类型即person,
    当子类重写的时候,对象的运行时类型是决定因素。即student1
      

  25.   

    f什么时候指向类了?它不是指向对象么?对象引用是指向具体的对象实例,连类和类的对象都混了,那么最好就不要用“内存分配”这中c语言了,否则反而麻烦。说到底,硬说Apple类实例中内嵌一个Fruit类实例,不然怎么“指向”?
      

  26.   

    我的笔记本已经贴上了那副图,能不能请阁下把那张图改一下,或是用图形说明一下Fruit f = new Apple();
      

  27.   

    只要了解两个概念:编译时类型和运行时类型,楼主所有的问题都可以不言自明。那个图要说的是 f 的运行时类型,很显然是 apple
      

  28.   

    如果从内存布局看,apple 对象包含了一个fruit 对象,说 f 指向 fruit 对象是没错的,但这个说法违背了阐释“运行时类型”这个意图