当前位置: 代码迷 >> C# >> C#根本语法学习(五)
  详细解决方案

C#根本语法学习(五)

热度:250   发布时间:2016-04-28 08:22:15.0
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.Int32obj2的实际类型为: System.Stringobj3的实际类型为: 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()\t 哺乳动物叫声各不相同"); 6         } 7     } 8  9     class Dog:Mammal10     {11         public void bark()12         {13             Console.WriteLine("Dog.bark()\t 狗的叫声汪汪汪");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()\t 哺乳动物叫声各不相同"); 6         } 7  8         public void walk() 9         {10             Console.WriteLine("Mammal.walk()\t 哺乳动物行走");11         }12     }13 14     class Dog:Mammal15     {16         public override void bark()17         {18             Console.WriteLine("Dog.bark()\t 狗的叫声汪汪汪");19         }20 21         public new void walk()22         {23             Console.WriteLine("Dog.walk()\t 狗奔跑很快");24         }25     }26     class Cat:Mammal27     {28         public override void bark()29         {30             Console.WriteLine("Cat.bark()\t猫的叫声喵喵喵");31         }32 33         public new void walk()34         {35             Console.WriteLine("Cat.walk()\t 猫行动敏捷");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,由于变量

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

 

  相关解决方案