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,由于变量

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

原文地址:https://www.cnblogs.com/numbqq/p/5286850.html