程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#基礎知識 >> C#基本語法學習(五)

C#基本語法學習(五)

編輯: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#只允許一個類僅從一個類繼承,但是一個類可以同時從多個接口繼承。

 

  變量的定義類型和實際類型:

  定義變量時的類型叫定義類型。變量被賦予值時的類型叫實際類型。變量的定義類型與實際類型不一定相同。如下

 object obj1, obj2;
 
 obj1 = 123;
 obj2 = "Hello";

  obj1和obj2定義類型都為object,但obj1實際類型是int,obj2實際類型是string。變量的類型都可以通過System.Object的GetType方法獲得,GetType返回一個System.Type類型的對象,用於描述變量的類型信息。由於變量可以多次被賦值,所以變量的

  實際類型在程序運行過程中是可以動態改變的。如下:

         static void Main(string[] args)
         {
             object obj1, obj2, obj3;
 
             Console.WriteLine("定義三個object類型變量");
             obj1 = 123;
             Console.WriteLine("將obj1賦值為123");
             obj2 = "Hello";
             Console.WriteLine("將obj2賦值為\"Hello\"");
             obj3 = DateTime.Now;
             Console.WriteLine("將obj3賦值為當前時間");
             Console.WriteLine("obj1的實際類型為: " + obj1.GetType().ToString());
             Console.WriteLine("obj2的實際類型為: " + obj2.GetType().ToString());
             Console.WriteLine("obj3的實際類型為: " + obj3.GetType().ToString());
 
             obj3 = new int[] { 1, 2, 3 };
             Console.WriteLine("將obj3賦值為一個整形數組");
             Console.WriteLine("obj3的實際類型為: " + obj3.GetType().ToString());
 
             Console.ReadKey();
         }

  運行結果為:

定義三個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[]來使用那麼會報錯,如下:

 obj3[0] = 1;//報錯

  

  基類和派生類之間的類型轉換

  派生類向基類的轉換是安全的,總可以成功;但是基類向派生類轉換時,只有當變量的實際類型是目標類型或或目標類型的派生類時,轉換才能成功,否則會拋出System.InvalidCastException異常。

 

  虛方法和多態

  如果基類和派生類都定義了相同的簽名的方法,那麼程序在運行時會調用那個方法呢?如下:

     class Mammal
     {
         public void bark()
         {
             Console.WriteLine("Mammal.bark()\t 哺乳動物叫聲各不相同");
         }
     }
 
     class Dog:Mammal
     {
         public void bark()
         {
             Console.WriteLine("Dog.bark()\t 狗的叫聲汪汪汪");
         }
     }
 
         static void Main(string[] args)
         {
             Mammal m = new Mammal();
             Dog d = new Dog();
 
             Console.WriteLine("Main 調用 Mammal.bark()");
             m.bark();
 
             Console.WriteLine("Main 調用 Dog.bark()");
             d.bark();
 
             Console.ReadLine();
         }

  運行結果

Main 調用 Mammal.bark()
Mammal.bark()    哺乳動物叫聲各不相同
Main 調用 Dog.bark()
Dog.bark()       狗的叫聲汪汪汪

  由結果可知調用Mammal類型變量的bark方法時Mammal類的bark方法被執行,調用Dog類的對象的bark方法時,Dog類的bark方法被執行。

 

  如果定義類型與實際類型不一致時,會怎麼樣呢?

             Mammal m;
             Dog d = new Dog();
 
             m = d;
             m.bark();
             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關鍵字重寫虛方法,可以使程序呈現多態性。

     class Mammal
     {
         public virtual void bark()
         {
             Console.WriteLine("Mammal.bark()\t 哺乳動物叫聲各不相同");
         }
 
         public void walk()
         {
             Console.WriteLine("Mammal.walk()\t 哺乳動物行走");
         }
     }
 
     class Dog:Mammal
     {
         public override void bark()
         {
             Console.WriteLine("Dog.bark()\t 狗的叫聲汪汪汪");
         }
 
         public new void walk()
         {
             Console.WriteLine("Dog.walk()\t 狗奔跑很快");
         }
     }
     class Cat:Mammal
     {
         public override void bark()
         {
             Console.WriteLine("Cat.bark()\t貓的叫聲喵喵喵");
         }
 
         public new void walk()
         {
             Console.WriteLine("Cat.walk()\t 貓行動敏捷");
         }
     }
         static void Main(string[] args)
         {
             Mammal m;
             Cat c = new Cat();
             Dog d = new Dog();
 
             Console.WriteLine("調用bark方法");
             m = c;
             m.bark();
             c.bark();
 
             m = d;
             m.bark();
             d.bark();
 
             Console.WriteLine("調用walk方法");
             m = c;
             m.walk();
             c.walk();
 
             m = d;
             m.walk();
             d.walk();
 
 
             Console.ReadLine();
         }

  運行結果

調用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,由於變量

  的值不同而表現出不同的行為,形成了多態性。

  1. 上一頁:
  2. 下一頁: