- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace NewVituslOverride
- {
- public class Father
- {
- public void Say()
- {
- Console.WriteLine("Father.Say()");
- }
- }
- public class Son : Father
- {
- new public void SonSay()
- {
- Console.WriteLine("Son.SonSay()");
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- Father father = new Father();
- Son son = new Son();
- father.Say();
- son.SonSay();
- Console.ReadLine();
- }
- }
- }
運行結果:
Father.Say()
Son.SonSay()
即運行結果沒問題,但編譯器會提出一個警告:子類Son中的方法SonSay()在父類中並不存在,沒必要使用new關鍵字。
2)第二種情況:父類中有一個方法,子類中也有一個同樣簽名的方法,但父類中該方法沒有使用使用virtual、override、abstract修飾,子類中該方法也沒有使用new或者override修飾。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace NewVituslOverride
- {
- public class Father
- {
- public void Say()
- {
- Console.WriteLine("Father.Say()");
- }
- }
- public class Son : Father
- {
- public void Say()
- {
- Console.WriteLine("Son.Say()");
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- Father[] persons = new Father[2];
- persons[0] = new Father();
- persons[1] = new Son();
- persons[0].Say();
- persons[1].Say();
- Console.ReadLine();
- }
- }
- }
Father.Say()
Father.Say()
爲什麼persons[1].Say()的運行結果也是Father.Say()呢?person[1]的實例類型明明是Son啊。因爲在c#中,如果子類中的方法簽名如果和父類中的方法簽名一致而不是override的(名字相同,參數格式和類型也一致,跟返回值沒關),那麼就自動隱式的標記爲new的方法了。而new出來的方法是不支持運行時綁定的,它是編譯時綁定,即跟聲明時類型有關,而跟運行時該對象的實際類型無關,即編譯時已經確定是調用父類的還是子類的方法了。
另外編譯器會給出一個警告:該方法簽名跟父類的一樣,如果是有意new的話,請加上new關鍵字。
3)第三種情況:子類的某個方法直接標識爲override的,而父類該方法並沒有聲明爲vitual、abstract或者override的。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace NewVituslOverride
- {
- public class Father
- {
- public void Say()
- {
- Console.WriteLine("Father.Say()");
- }
- }
- public class Son : Father
- {
- public override void Say()
- {
- Console.WriteLine("Son.Say()");
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- Father[] persons = new Father[2];
- persons[0] = new Father();
- persons[1] = new Son();
- persons[0].Say();
- persons[1].Say();
- Console.ReadLine();
- }
- }
- }
'NewVituslOverride.Son.Say()': cannot override inherited member 'NewVituslOverride.Father.Say()' because it is not marked virtual, abstract, or override。
編譯錯誤內容很清楚,我就不解釋了。
本例子說明override不是隨便用的,必須父類的方法、屬性、索引、事件是abstract、vitual或者override的時候,子類才能override它。
4)第四種情況:子類new父類中的方法。
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace NewVituslOverride
- {
- public class Father
- {
- public void Say()
- {
- Console.WriteLine("Father.Say()");
- }
- }
- public class Son : Father
- {
- public new void Say()
- {
- Console.WriteLine("Son.Say()");
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- Father[] persons = new Father[2];
- persons[0] = new Father();
- persons[1] = new Son();
- persons[0].Say();
- persons[1].Say();
- Console.ReadLine();
- }
- }
- }
Father.Say()
Father.Say()
如果第二種情況看懂了,就知道這個例子的運行結果和第二種情況一樣的。只是這次代碼用new把意思說的明明白白,子類我就是要隱藏父類的方法Say()。由於person[1]的聲明類型是Father(雖然實際類型是Son),所以還是因爲編譯時綁定的原因,person[1].Say()跑出來的結果還是Father.Say()。
5)父類方法使用vitual,子類使用override重寫
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- namespace NewVituslOverride
- {
- public class Father
- {
- public virtual void Say()
- {
- Console.WriteLine("Father.Say()");
- }
- }
- public class Son : Father
- {
- public override void Say()
- {
- Console.WriteLine("Son.Say()");
- }
- }
- class Program
- {
- static void Main(string[] args)
- {
- Father[] persons = new Father[2];
- persons[0] = new Father();
- persons[1] = new Son();
- persons[0].Say();
- persons[1].Say();
- Console.ReadLine();
- }
- }
- }
Father.Say()
Son.Say()
這次雖然persons[1]的聲明類型是Father,但因爲Say()是vitual的,需要運行時根據對象的實際類型來調用,因爲運行時person[1]的實際類型是Son,所以運行的是Son的Say()。
我們來仔細看下這個例子生成的IL代碼:
Father類:
- .class public auto ansi beforefieldinit Father
- extends [mscorlib]System.Object
- {
- .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
- {
- .maxstack 8
- L_0000: ldarg.0
- L_0001: call instance void [mscorlib]System.Object::.ctor()
- L_0006: ret
- }
- .method public hidebysig newslot virtual instance void Say() cil managed
- {
- .maxstack 8
- L_0000: nop
- L_0001: ldstr "Father.Say()"
- L_0006: call void [mscorlib]System.Console::WriteLine(string)
- L_000b: nop
- L_000c: ret
- }
- }
我們接着來看Son類的IL代碼:
- .class public auto ansi beforefieldinit Son
- extends NewVituslOverride.Father
- {
- .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
- {
- .maxstack 8
- L_0000: ldarg.0
- L_0001: call instance void NewVituslOverride.Father::.ctor()
- L_0006: ret
- }
- .method public hidebysig virtual instance void Say() cil managed
- {
- .maxstack 8
- L_0000: nop
- L_0001: ldstr "Son.Say()"
- L_0006: call void [mscorlib]System.Console::WriteLine(string)
- L_000b: nop
- L_000c: ret
- }
- }
接着我們來看下Program類的IL代碼:
- .class private auto ansi beforefieldinit Program
- extends [mscorlib]System.Object
- {
- .method public hidebysig specialname rtspecialname instance void .ctor() cil managed
- {
- .maxstack 8
- L_0000: ldarg.0
- L_0001: call instance void [mscorlib]System.Object::.ctor()
- L_0006: ret
- }
- .method private hidebysig static void Main(string[] args) cil managed
- {
- .entrypoint
- .maxstack 3
- .locals init (
- [0] class NewVituslOverride.Father[] persons)
- L_0000: nop
- L_0001: ldc.i4.2
- L_0002: newarr NewVituslOverride.Father
- L_0007: stloc.0
- L_0008: ldloc.0
- L_0009: ldc.i4.0
- L_000a: newobj instance void NewVituslOverride.Father::.ctor()
- L_000f: stelem.ref
- L_0010: ldloc.0
- L_0011: ldc.i4.1
- L_0012: newobj instance void NewVituslOverride.Son::.ctor()
- L_0017: stelem.ref
- L_0018: ldloc.0
- L_0019: ldc.i4.0
- L_001a: ldelem.ref
- L_001b: callvirt instance void NewVituslOverride.Father::Say()
- L_0020: nop
- L_0021: ldloc.0
- L_0022: ldc.i4.1
- L_0023: ldelem.ref
- L_0024: callvirt instance void NewVituslOverride.Father::Say()
- L_0029: nop
- L_002a: call string [mscorlib]System.Console::ReadLine()
- L_002f: pop
- L_0030: ret
- }
- }
在Main函數中,先聲明瞭一個Father[]數組,接着設置該數組的長度爲2,接着創建了一個Father對象和一個Son對象。
關鍵是這兩個對象調用Say()方法。我們可以看到是使用callvirl命令的,而不是call命令,使用callvirl命令就要求運行時檢查對象的實際類型,然後調用該類型的對應方法。