接上一篇.net框架读书笔记---引用参数(ref/out),
一、虚方法调用机理
{
class Program
{
static void Main( string [] args)
{
SomeBaseClass sbc = new SomeClass();
sbc.SomeFun();
SomeBaseClass sbcOwn = new SomeBaseClass();
sbcOwn.SomeFun();
SomeClass sc = new SomeClass();
sc.SomeFun();
}
}
public class SomeBaseClass
{
public virtual void SomeFun()
{
Console.WriteLine( " Base " );
}
}
public class SomeClass : SomeBaseClass
{
public override void SomeFun()
{
Console.WriteLine( " inherited " );
}
}
}
上面代码展示了,对于虚方法的调用。
通过检查元数据,CLR可以确定一个非静态方法是否为一个虚方法。然而,CLR在调用方法时并不使用该信息,CLR提供了两个IL指令来调用方法:call和callvirt.其中call指令根据引用变量的类型(引用变量的动态类型,实际类型)来调用一个方法,而callvirt指令根据引用变量的对象类型来调用一个方法。当编译源代码时,编译器知道代码是否在调用一个虚方法,并根据此产生call和callvirt指令。
不管最终通过call还是callvirt来调用一个实例方法,所有的实例方法都会接受一个隐藏的this 指针作为方法d哦第一个参数。其中this指针指向当前正在操作的对象。
二、虚方法的版本问题
假设CompanyA设计一个类型Phone。
{
class Phone
{
public void Dial()
{
Console.WriteLine( " Phone.Dial " );
}
}
}
再假设CompanyB使用CompanyA的Phone作为基类又定义了一个类型BetterPhone。
{
class BetterPhone : CompanyA.Phone
{
public void Dial()
{
Console.WriteLine( " BetterPhone.Dial " );
EstablishConnection();
base .Dial();
}
protected virtual void EstablishConnection()
{
Console.WriteLine( " BetterPhone.EstablishConnection " );
}
}
}
当编译以上代码时会爆出警告“Warning 1 'CompanyB.BetterPhone.Dial()' hides inherited member 'CompanyA.Phone.Dial()'. Use the new keyword if hiding was intended.”,该警告告知开发人员BetterPhone中在定义Dial方法会隐藏Phone中的Dial方法。编译器还告知我们通过在BetterPhone类的Dial定义前添加new关键字可以消除这种警告。修正后:
{
class BetterPhone : CompanyA.Phone
{
public new void Dial()
{
Console.WriteLine( " BetterPhone.Dial " );
EstablishConnection();
base .Dial();
}
protected virtual void EstablishConnection()
{
Console.WriteLine( " BetterPhone.EstablishConnection " );
}
}
}
上面代码警告消失。上面代码演示了对于非虚方法的版本控制。
虚方法的版本控制其实是一样的,假设CompanyA也增加虚方法,看下面代码
{
class Phone
{
public void Dial()
{
Console.WriteLine( " Phone.Dial " );
EstablishConnection();
}
protected virtual void EstablishConnection()
{
Console.WriteLine( " Phone.EstablishConnection " );
}
}
}
保持CompanyB的方法不要改变,编译后仍然会出现上面的警告,解决办法在CompanyB的虚方法前面增加new关键字:
{
class BetterPhone : CompanyA.Phone
{
public new void Dial()
{
Console.WriteLine( " BetterPhone.Dial " );
EstablishConnection();
base .Dial();
}
protected new virtual void EstablishConnection()
{
Console.WriteLine( " BetterPhone.EstablishConnection " );
}
}
}
ps:new关键字告诉编译器,子类中的方法虽然与父类方法一样(签名,参数),但是该方法与基类中的方法没有任何关系。
也有另外一种情况,CompanyB可能在得到CompanyA新版本后,认为COmpany。Phone的Dial和EstablishConnection是其期望的,那么CompanyB.BetterPhone可以完全删除BetterPhone中的Dial方法,由于CompanyB希望告知编译器BetterPhone中的EstablistConnnection和Phone中的EstablishConnection是相关联的,那么new关键字必须要去掉,还需要将BetterPhone的virtual变为override(重写),如下:
{
class BetterPhone : CompanyA.Phone
{
// 删除Dial方法,直接从基类继承Dial
// 删除new并将virtual修改为override
// 该方法将和基类的EstablishConnection建立联系
protected override void EstablishConnection()
{
Console.WriteLine( " BetterPhone.EstablishConnection " );
}
}
}
有兴趣,大家可以看看下面执行的结果是什么。
{
class Program
{
static void Main( string [] args)
{
Base bsA = new InheritedA();
bsA.Fun(); // ??
Base bsB = new InheritedB();
bsB.Fun(); // ??
}
}
class Base
{
public virtual void Fun()
{
Console.WriteLine( " BaseFun " );
}
}
class InheritedA :Base
{
public new void Fun()
{
Console.WriteLine( " InteritedFunA " );
}
}
class InheritedB : Base
{
public override void Fun()
{
Console.WriteLine( " InteritedFunB " );
}
}
}
??处的输出时什么