• C#基本语法学习(五)


    继承和多态

      面向对象方法中的继承体现了现实世界中的“一般特殊关系”。基类代表一般性事物,而派生类是一种特殊的基类,是对基类的补充和细化。不同的派生类执行同一个方法时,会出现不同的行为,这就是多态。

    实现继承

      C#中用如下语法实现继承:

      class 派生类:基类 {类的成员}

      eg:public class MyButton:System.Windows.Forms.Button {}

      C#中所有的类都是直接或间接从System.Object类派生来的,如果定义一个类时没有指明基类,那么这个类的基类就是System.Object。.NET Framework中所有的类都是直接或间接派生自System.Object,甚至包括像int、string等简单的类型也是。

    因此C#中所有的类都是直接或间接继承自System.Object类,从而也都拥有System.Object类中所定义的公共成员。

      C#只允许一个类仅从一个类继承,但是一个类可以同时从多个接口继承。

      变量的定义类型和实际类型:

      定义变量时的类型叫定义类型。变量被赋予值时的类型叫实际类型。变量的定义类型与实际类型不一定相同。如下

    1 object obj1, obj2;
    2 
    3 obj1 = 123;
    4 obj2 = "Hello";

      obj1和obj2定义类型都为object,但obj1实际类型是int,obj2实际类型是string。变量的类型都可以通过System.Object的GetType方法获得,GetType返回一个System.Type类型的对象,用于描述变量的类型信息。由于变量可以多次被赋值,所以变量的

      实际类型在程序运行过程中是可以动态改变的。如下:

     1         static void Main(string[] args)
     2         {
     3             object obj1, obj2, obj3;
     4 
     5             Console.WriteLine("定义三个object类型变量");
     6             obj1 = 123;
     7             Console.WriteLine("将obj1赋值为123");
     8             obj2 = "Hello";
     9             Console.WriteLine("将obj2赋值为"Hello"");
    10             obj3 = DateTime.Now;
    11             Console.WriteLine("将obj3赋值为当前时间");
    12             Console.WriteLine("obj1的实际类型为: " + obj1.GetType().ToString());
    13             Console.WriteLine("obj2的实际类型为: " + obj2.GetType().ToString());
    14             Console.WriteLine("obj3的实际类型为: " + obj3.GetType().ToString());
    15 
    16             obj3 = new int[] { 1, 2, 3 };
    17             Console.WriteLine("将obj3赋值为一个整形数组");
    18             Console.WriteLine("obj3的实际类型为: " + obj3.GetType().ToString());
    19 
    20             Console.ReadKey();
    21         }

      运行结果为:

    定义三个object类型变量
    将obj1赋值为123
    将obj2赋值为"Hello"
    将obj3赋值为当前时间
    obj1的实际类型为: System.Int32
    obj2的实际类型为: System.String
    obj3的实际类型为: System.DateTime
    将obj3赋值为一个整形数组
    obj3的实际类型为: System.Int32[]

      从运行结果来看,3个变量的定义类型都为object,实际类型分别为Int32、String和DateTime,而且obj3实际类型发生了变化,从DateTime变为int[]。

      变量只能按照定义的类型来使用。上面例子中obj3定义类型为object,就只能当object类型来使用,虽然后面实际类型为int[],如果把obj3当int[]来使用那么会报错,如下:

    1 obj3[0] = 1;//报错

      

      基类和派生类之间的类型转换

      派生类向基类的转换是安全的,总可以成功;但是基类向派生类转换时,只有当变量的实际类型是目标类型或或目标类型的派生类时,转换才能成功,否则会抛出System.InvalidCastException异常。

      虚方法和多态

      如果基类和派生类都定义了相同的签名的方法,那么程序在运行时会调用那个方法呢?如下:

     1     class Mammal
     2     {
     3         public void bark()
     4         {
     5             Console.WriteLine("Mammal.bark()	 哺乳动物叫声各不相同");
     6         }
     7     }
     8 
     9     class Dog:Mammal
    10     {
    11         public void bark()
    12         {
    13             Console.WriteLine("Dog.bark()	 狗的叫声汪汪汪");
    14         }
    15     }
    16 
    17         static void Main(string[] args)
    18         {
    19             Mammal m = new Mammal();
    20             Dog d = new Dog();
    21 
    22             Console.WriteLine("Main 调用 Mammal.bark()");
    23             m.bark();
    24 
    25             Console.WriteLine("Main 调用 Dog.bark()");
    26             d.bark();
    27 
    28             Console.ReadLine();
    29         }

      运行结果

    Main 调用 Mammal.bark()
    Mammal.bark()    哺乳动物叫声各不相同
    Main 调用 Dog.bark()
    Dog.bark()       狗的叫声汪汪汪

      由结果可知调用Mammal类型变量的bark方法时Mammal类的bark方法被执行,调用Dog类的对象的bark方法时,Dog类的bark方法被执行。

      如果定义类型与实际类型不一致时,会怎么样呢?

    1             Mammal m;
    2             Dog d = new Dog();
    3 
    4             m = d;
    5             m.bark();
    6             d.bark();

      运行结果

    Mammal.bark()    哺乳动物叫声各不相同
    Dog.bark()       狗的叫声汪汪汪

      从运行结果可以看出,虽然m和d是同一个对象,但由于定义对象不同,掉用bark执行的代码也不一样。bark方法实际执行的代码是由定义类型决定的。所以m.bark()调用Mammal的bark方法,d.bark()调用Dog的bark方法。

      

      在很多时候,开发人员并不希望程序这样运行,而是希望程序能够根据变量的实际类型来调用相应的方法。这样对于同一个Mammal类型的变量m,当其实际类型为不同的派生类时,调用m.bark()方法会产生不同的行为,这就是多态。

      当基类和派生类都定义了相同签名的方法时,C#允许开发人员明确指定哪个方法应该被调用。是根据定义类型调用方法还是根据实际类型调用方法,C#通过虚方法、方法重写和方法隐藏实现这个功能。

      如果想让程序在运行时根据变量的定义类型来决定调用那个方法,可以通过方法隐藏来实现;如果想让程序实现多态性,即在运行时根据变量的实际类型调用相应的方法,那么可以通过虚方法和方法重写实现。

      

      定义方法时使用new关键字可以隐藏基类具有相同签名的方法,语法如下:

      访问修饰符 new 返回值类型 方法名(参数列表){方法体}

      上述代码预定一个普通方法的唯一区别是多了一个new关键字,new关键字表明这个方法将隐藏基类中相同签名的方法。new关键字可以放在访问修饰符的前面或后面都可以。

      使用virtual关键字可以定义一个虚方法,虚方法可以在派生类中被重写。定义虚方法语法如下:

      访问修饰符 virtual 返回值类型 方法名(参数列表) {方法体}

      派生类使用override关键字重写基类中的虚方法,语法如下:

      访问修饰符 override 返回值类型 方法名(参数列表) {方法体}

      在基类中使用virtual关键字定义虚方法,在派生类中使用override关键字重写虚方法,可以使程序呈现多态性。

     1     class Mammal
     2     {
     3         public virtual void bark()
     4         {
     5             Console.WriteLine("Mammal.bark()	 哺乳动物叫声各不相同");
     6         }
     7 
     8         public void walk()
     9         {
    10             Console.WriteLine("Mammal.walk()	 哺乳动物行走");
    11         }
    12     }
    13 
    14     class Dog:Mammal
    15     {
    16         public override void bark()
    17         {
    18             Console.WriteLine("Dog.bark()	 狗的叫声汪汪汪");
    19         }
    20 
    21         public new void walk()
    22         {
    23             Console.WriteLine("Dog.walk()	 狗奔跑很快");
    24         }
    25     }
    26     class Cat:Mammal
    27     {
    28         public override void bark()
    29         {
    30             Console.WriteLine("Cat.bark()	猫的叫声喵喵喵");
    31         }
    32 
    33         public new void walk()
    34         {
    35             Console.WriteLine("Cat.walk()	 猫行动敏捷");
    36         }
    37     }
    38         static void Main(string[] args)
    39         {
    40             Mammal m;
    41             Cat c = new Cat();
    42             Dog d = new Dog();
    43 
    44             Console.WriteLine("调用bark方法");
    45             m = c;
    46             m.bark();
    47             c.bark();
    48 
    49             m = d;
    50             m.bark();
    51             d.bark();
    52 
    53             Console.WriteLine("调用walk方法");
    54             m = c;
    55             m.walk();
    56             c.walk();
    57 
    58             m = d;
    59             m.walk();
    60             d.walk();
    61 
    62 
    63             Console.ReadLine();
    64         }

      运行结果

    调用bark方法
    Cat.bark()      猫的叫声喵喵喵
    Cat.bark()      猫的叫声喵喵喵
    Dog.bark()       狗的叫声汪汪汪
    Dog.bark()       狗的叫声汪汪汪
    调用walk方法
    Mammal.walk()    哺乳动物行走
    Cat.walk()       猫行动敏捷
    Mammal.walk()    哺乳动物行走
    Dog.walk()       狗奔跑很快()

      由运行结果可以看出用new关键字进行方法隐藏后,被调用的方法由变量的定义类型决定。虽然m实际是Dog类(或Cat类)的实例,但是由于m被定义成一个Mammal类型的变量,所以当调用m.walk()方法时,总是调用Mammal类的walk方法,

      而不会调用Cat或Dog类的walk方法。

      对于使用virtual和override关键字声明的方法,再调用时由变量的实际类型决定调用那个类的相应方法。m先后被赋予Cat类型和Dog类型的值,在调用bark方法时,先后调用了Cat类的bark方法和Dog类的bark方法。同一段代码m.bark,由于变量

      的值不同而表现出不同的行为,形成了多态性。

  • 相关阅读:
    Codeforces 735C:Tennis Championship(数学+贪心)
    HDU 5934:Bomb(强连通缩点)
    BZOJ-2743 采花
    BZOJ-1878 HH的项链
    BZOJ-1798 维护序列
    BZOJ-1911 特别行动队
    BZOJ-1010 玩具装箱
    BZOJ-1096 仓库建设
    BZOJ-1012 最大数
    ZOJ 3696 Alien's Organ(泊松定理,期望值)
  • 原文地址:https://www.cnblogs.com/numbqq/p/5286850.html
Copyright © 2020-2023  润新知