先上測試代碼:ide
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TestVirt { class Program { static void Main(string[] args) { A c1 = new C(); c1.Foo(); C c2 = new C(); c2.Foo(); Console.ReadLine(); } } class A { public virtual void Foo() { Console.WriteLine("Call on A.Foo()"); } } class B : A { public override void Foo() { Console.WriteLine("Call on B.Foo() "); } } class C : B { public new void Foo() { Console.WriteLine("Call on C.Foo()"); } } }
可能你對C c2 = new C();這個的結果沒有什麼疑問,可是對A c1 = new C();的結果百思不解。呵呵,咱們慢慢來看這個區別,先來看看最終的IL代碼是什麼樣子的:佈局
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代碼大小 34 (0x22) .maxstack 1 .locals init ([0] class TestVirt.A c1, [1] class TestVirt.C c2) IL_0000: nop IL_0001: newobj instance void TestVirt.C::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: callvirt instance void TestVirt.A::Foo() IL_000d: nop IL_000e: newobj instance void TestVirt.C::.ctor() IL_0013: stloc.1 IL_0014: ldloc.1 IL_0015: callvirt instance void TestVirt.C::Foo() IL_001a: nop IL_001b: call string [mscorlib]System.Console::ReadLine() IL_0020: pop IL_0021: ret } // end of method Program::Main
根據IL的結果,咱們明顯能夠看到,兩次調用不一樣的地方就在於一個是Call的A的Foo,一個是C的Foo。測試
可是這裏你注意了:咱們new的是一樣的一個對象,他們具備一樣的內存佈局。spa
用WinDbg來看看咱們new出來對象的MethodTable是長什麼樣子的吧:code
看到沒有,這個C對象的方法表裏面同時包括了C本身定義的Foo和上一層次父對象的Foo方法。對象
結合IL的結果和C對象的方法表的Dump結果,相信看官已經明白爲啥兩次調用爲啥會用不一樣了吧。blog
算了,仍是簡單描述一下吧:首先根據il的結果明顯兩次調用請求的方法是不一樣的;其次,你能夠看到咱們的C對象引用的方法表裏面確實有兩個Foo方法。內存
呵呵,這樣一樣類型的對象對不一樣方法調用的請求是否是就能夠分開了呢?固然是!ci
PS:可能會有人問:爲啥我請求的A.Foo()你這個MethodTable裏面沒有呢?string
腦補下吧哥:被B給override了。