.net框架读书笔记---虚方法

接上一篇.net框架读书笔记---引用参数(ref/out)

一、虚方法调用机理

  

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
namespace VirtualFunction
{
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。

 
  
namespace CompanyA
{
class Phone
{
public void Dial()
{
Console.WriteLine(
" Phone.Dial " );
}
}
}

 

  再假设CompanyB使用CompanyA的Phone作为基类又定义了一个类型BetterPhone。

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
namespace CompanyB
{
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关键字可以消除这种警告。修正后:

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
namespace CompanyB
{
class BetterPhone : CompanyA.Phone
{
public new void Dial()
{
Console.WriteLine(
" BetterPhone.Dial " );
EstablishConnection();
base .Dial();
}
protected virtual void EstablishConnection()
{
Console.WriteLine(
" BetterPhone.EstablishConnection " );
}

}
}

 

上面代码警告消失。上面代码演示了对于非虚方法的版本控制。

  虚方法的版本控制其实是一样的,假设CompanyA也增加虚方法,看下面代码

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
namespace CompanyA
{
class Phone
{
public void Dial()
{
Console.WriteLine(
" Phone.Dial " );
EstablishConnection();
}

protected virtual void EstablishConnection()
{
Console.WriteLine(
" Phone.EstablishConnection " );
}
}
}

  保持CompanyB的方法不要改变,编译后仍然会出现上面的警告,解决办法在CompanyB的虚方法前面增加new关键字:

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
namespace CompanyB
{
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(重写),如下:

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
namespace CompanyB
{
class BetterPhone : CompanyA.Phone
{

// 删除Dial方法,直接从基类继承Dial

// 删除new并将virtual修改为override
// 该方法将和基类的EstablishConnection建立联系
protected override void EstablishConnection()
{
Console.WriteLine(
" BetterPhone.EstablishConnection " );
}

}
}

有兴趣,大家可以看看下面执行的结果是什么。

ContractedBlock.gif ExpandedBlockStart.gif 代码
 
   
namespace VirtualFunction
{

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 " );
}
}
}

??处的输出时什么

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值