當類中的方法聲明前加上了virtual 修飾符,我們稱之為虛方法,反之為非虛。使用了virtual 修飾符后,不允許再有static, abstract, 或override 修飾符。
示例1:帶有虛方法的類
using System ;
public class DrawingBase
{
public virtual void Draw( )
{ Console.WriteLine("這是一個虛方法!") ; }
}
說明:這里定義了DrawingBase類。這是個可以讓其他對象繼承的基類。該類有一個名為Draw( )的方法。Draw( )方法帶有一個virtual修飾符,該修飾符表明:該基類的派生類可以重載該方法。DrawingBase類的 Draw( )方法完成如下事情:輸出語句"這是一個虛方法!"到控制台。
示例2:帶有重載方法的派生類
using System ;
public class Line : DrawingBase
{
public override void Draw( )
{ Console.WriteLine("畫線.") ; }
}
public class Circle : DrawingBase
{
public override void Draw( )
{ Console.WriteLine("畫圓.") ; }
}
public class Square : DrawingBase
{
public override void Draw( )
{ Console.WriteLine("畫正方形.") ; }
}
說明:上面程序定義了三個類。這三個類都派生自DrawingBase類。每個類都有一個同名Draw( )方法,這些Draw( )方法中的每一個都有一個重載修飾符。重載修飾符可讓該方法在運行時重載其基類的虛方法,實現這個功能的條件是:通過基類類型的指針變量來引用該類。
對於非虛的方法,無論被其所在類的實例調用,還是被這個類的派生類的實例調用,方法的執行方式不變。而對於虛方法,它的執行方式可以被派生類改變,這種改變是通過方法的重載來實現的。
下面的例子說明了虛方法與非虛方法的區別。
1 class A 2 { 3 public void F() { Console.WriteLine("A.F"); } 4 public virtual void G() { Console.WriteLine("A.G"); } //定義虛方法 5 } 6 class B : A 7 { 8 new public void F() { Console.WriteLine("B.F"); } //這里的方法則不能重寫,因為基類中的F()方法不是虛方法。 9 public override void G() { Console.WriteLine("B.G"); } //重寫虛方法 10 } 11 static void Main(string[] args) 12 { 13 FashionCoat show = new FashionCoat(); 14 show.ShowCoat(); 15 BusinessFactory showbusiness = new BusinessFactory(); 16 showbusiness.CreatCoat().ShowCoat(); 17 18 B b = new B(); 19 A a = b; //A對象指向B對象 20 b.F(); //顯示結果B.F 21 a.F(); //顯示結果A.F 22 b.G(); //顯示結果B.G 23 a.G(); //顯示結果B.G 實際這里調用的是不是基類中的G方法,而是派生類中重寫過的G方法。 24 Console.ReadKey(); 25 }
例子中,A 類提供了兩個方法:非虛的F 和虛方法G 。類B 則提供了一個新的非虛的方法F, 從而覆蓋了繼承的F; 類B 同時還重載了繼承的方法G 。那么輸出
應該是:A.F B.F B.G B.G
注意到本例中,方法a.G( ) 實際調用了B.G,而不是A.G,這是因為編譯時值為A,但運行時值為B ,所以B 完成了對方法的實際調用
自己想象
得出:派生類與基類,當定義基類對象指向派生類象時(基類類名 基類對象實例=派生類對象實例),當執行基類對象.函數只有定義為虛的函數(方法),派生類才能重載(調用自己定義的重載函法.)而不是虛方法時當調用基類對象.函數(這個函數名在派生類中重復定義)時,則調用的是基類中的(函數(也可理解虛方法)),而派生類中定義的被隱藏(也不是不會被執行).. 這就是定義虛方法的用處,派生類可以利用重載改變基類中的虛方法
當實例方法聲明包含 virtual 修飾符時,稱該方法為虛擬方法。不存在 virtual 修飾符時,稱該方法為非虛擬方法。
非虛擬方法的實現是不變的:無論是在聲明它的類的實例上調用該方法還是在派生類的實例上調用,實現都是相同的。與此相反,虛擬方法的實現可以由派生類取代。取代所繼承的虛擬方法之實現的過程稱為重寫方法
在虛擬方法調用中,為其進行調用的實例的運行時類型確定要調用的實際方法實現。在非虛擬方法調用中,實例的編譯時類型是決定性因素。准確地說,當在具有編譯時類型
C 和運行時類型 R 的實例(其中 R 為 C 或者從 C 派生的類)上用參數列表 A 調用名為 N 的方法時,調用按下面這樣處理:
首先,將重載決策應用於 C、N 和 A,以從在 C 中聲明和由 C 繼承的方法集中選擇一個特定方法 M。
然后,如果 M 為非虛擬方法,則調用 M。
否則,M 為虛擬方法,調用就 R 而言 M 的派生程度最大的實現。
對於在類中聲明或者由類繼承的每個虛擬方法,存在一個就該類而言的派生程度最大的實現。就類 R 而言虛擬方法 M 的派生度最大的實現按下面這樣確定:
如果 R 包含 M 的引入 virtual 聲明,則這是 M 的派生程度最大的實現。
否則,如果 R 包含 M 的 override,則這是 M 的派生程度最大的實現。
否則,M 的派生程度最大的實現與 R 的直接基類的派生程度最大的實現相同。
下列實例闡釋虛擬方法和非虛擬方法之間的區別:
class A
{
public void F() { Console.WriteLine("A.F"); }
public virtual void G() { Console.WriteLine("A.G"); }
}
class B: A
{
new public void F() { Console.WriteLine("B.F"); }
public override void G() { Console.WriteLine("B.G"); }
}
class Test
{
static void Main() {
B b = new B();
A a = b;
a.F();
b.F();
a.G();
b.G();
}
}
在該示例中,A 引入一個非虛擬方法 F 和一個虛擬方法 G。類 B 引入一個新的非虛擬方法 F,從而隱藏了繼承的 F,並且還重寫了繼承的方法 G。此例產
生下列輸出:
A.F
B.F
B.G
B.G
請注意,語句 a.G() 調用 B.G 而不是 A.G。這是因為是實例的運行時類型(即 B)而不是實例的編譯時類型(即 A)確定要調用的實際方法實現。
由於允許方法隱藏繼承的方法,因此類可以包含具有相同簽名的若干個虛擬方法。由於除派生程度最大的方法外全部都被隱藏,因此這不會造成多義性問題。在下面的示例中,
1 class A 2 { 3 public virtual void F() { Console.WriteLine("A.F"); } 4 } 5 class B : A 6 { 7 public override void F() { Console.WriteLine("B.F"); } //重寫A類中的虛方法 8 } 9 class C : B 10 { 11 new public virtual void F() { Console.WriteLine("C.F"); } //隱藏B類中重寫過的方法,只剩下這里的虛方法會被顯示,但被D重寫。 12 } 13 class D : C 14 { 15 public override void F() { Console.WriteLine("D.F"); } //重寫C類中的虛方法 16 } 17 static void Main(string[] args) 18 { 19 D d = new D(); 20 A a = d; 21 B b = d; 22 C c = d; 23 a.F(); //A類中的虛方法被重寫為B類中的F(),所以顯示結果為BF。 24 b.F(); //B類中的F()方法被調用,所以顯示BF 25 c.F(); //B類中的重寫方法被隱藏,調用C類中的虛方法,兒虛方法被D重寫,故調用D類的F(),顯示結果為DF 26 d.F(); //尋找基類C中的虛方法,C的基類A中的虛方法不被執行,也就說明虛方法重載只能從上一輩重載 27 Console.ReadKey(); 28 }
C 類和 D 類包含兩個具有相同簽名的虛擬方法:A 引入的虛擬方法和 C 引入的虛擬方法。C 引入的方法隱藏從 A 繼承的方法。因此,D 中的重寫聲明重
寫 C 引入的方法,而 D 不可能重寫 A 引入的方法。(D重載老爸C,不能從老爺那重載A)此例產生下列輸出:
B.F
B.F
D.F
D.F
請注意,可以通過訪問 D 的實例(通過在其中未隱藏方法的派生程度較小的類型)調用隱藏的虛擬方法。
虛方法在形式上在方法名前加virtual修飾.比如:
在C#中這樣定義一個:
public class baseclass{
protected virtual int MyFunction(){
return -1
}
}
虛方法一般在基類定義,在派生類中實現具體操作,派生類實現該方法時,要用override修飾.如:
public class myclass:baseclass{
protected override int MyFunction(){
return 1;
}
}
創建實例時,可用基類的變量而調用子類的構造函數來實例.
如:
baseclass bc;
bc = new myclass();
int v=bc.MyFunction();//這里返回1,調用的時派生類的方法.
這樣有什么好處呢?
比如往往在程序開始設計的類時,先進行抽象性的設計,比如:很多表都有增加、刪除、查詢操作,根據這樣共同點,可以設計一個基類:
public class baseclass{
public virtual Dataview Select(){return null;}
public virtual bool Delete(){return false;}
public virtual bool add(){return false;}
}
這樣,調用基類的虛方法來執行派生類的具體操作。而在基類調用時,並不需要知道派生類是怎么樣實現的。因為不同的派生類可能實現的方式不一樣。但調用的方式是一樣的
,這就大大增加代碼的可維護性,脈絡較清淅有條理。
還有一種情需要是在程序運行時,需要派生類的賦值。