虚拟方法

若一个实例方法的声明中含有 virtual 修饰符,则称该方法为虚拟方法。若其中没有 virtual 修饰符,则称该方法为非虚拟方法。

非虚拟方法的实现是不会变的:无论是在声明它的类的实例上调用该方法还是在派生类的实例上调用,实现都是相同的。与此相反,一个虚拟方法的实现可以由派生类取代。取代所继承的虚拟方法的实现的过程称为重写该方法(第 10.5.4 节)

在一个虚拟方法调用中,该调用所涉及的那个实例的运行时类型确定了要被调用的究竟是该方法的哪一个实现。在非虚拟方法调用中,相关的实例的编译时类型是决定性因素。准确地说,当在具有编译时类型 C 和运行时类型 R 的实例(其中 R 为 C 或者从 C 派生的类)上用参数列表 A 调用名为 N 的方法时,调用按下述规则处理:

  • 首先,将重载决策应用于 C、N 和 A,以从在 C 中声明的和由 C 继承的方法集中选择一个特定的方法 M。第 7.5.5.1 节对此进行了描述。
  • 然后,如果 M 为非虚拟方法,则调用 M。
  • 否则(M 为虚拟方法),就会调用就 R 而言 M 的派生程度最大的那个实现。

对于在一个类中声明的或者由类继承的每个虚拟方法,存在一个就该类而言的派生程度最大的实现。就类 R 而言虚拟方法 M 的派生度最大的实现按下述规则确定:

  • 如果 R 中含有关于 M 的 virtual 声明,则这是 M 的派生程度最大的实现。
  • 否则,如果 R 中含有关于 M 的 override 声明,则这是 M 的派生程度最大的实现。
  • 否则,就 R 而言 M 的派生程度最大的实现与就 R 的直接基类而言 M 的派生程度最大的实现相同。

下列实例阐释虚拟方法和非虚拟方法之间的区别:

None.gif using  System;
None.gif
class  A
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif   
public void F() dot.gif{ Console.WriteLine("A.F"); }
ExpandedSubBlockStart.gifContractedSubBlock.gif   
public virtual void G() dot.gif{ Console.WriteLine("A.G"); }
ExpandedBlockEnd.gif}

None.gif
class  B: A
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif   
new public void F() dot.gif{ Console.WriteLine("B.F"); }
ExpandedSubBlockStart.gifContractedSubBlock.gif   
public override void G() dot.gif{ Console.WriteLine("B.G"); }
ExpandedBlockEnd.gif}

None.gif
class  Test
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif   
static void Main() dot.gif{
InBlock.gif      B b 
= new B();
InBlock.gif      A a 
= b;
InBlock.gif      a.F();
InBlock.gif      b.F();
InBlock.gif      a.G();
InBlock.gif      b.G();
ExpandedSubBlockEnd.gif   }

ExpandedBlockEnd.gif}

None.gif

在该示例中,A 引入一个非虚拟方法 F 和一个虚拟方法 G。类 B 引入一个新的非虚拟方法 F,从而隐藏了继承的 F,并且还重写了继承的方法 G。此例产生输出:

 
 
None.gif .F
None.gifB.F
None.gifB.G
None.gifB.G

请注意,语句 a.G() 实际调用的是 B.G 而不是 A.G。这是因为,对调用哪个实际方法实现起决定作用的是该实例的运行时类型(即 B),而不是该实例的编译时类型(即 A)。

由于一个类中声明的方法可以隐藏继承来的方法,因此同一个类中可以包含若干个具有相同签名的虚拟方法。这不会造成多义性问题,因为除派生程度最大的那个方法外,其他方法都被隐藏起来了。在下面的示例中

None.gif using  System;
None.gif
class  A
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif   
public virtual void F() dot.gif{ Console.WriteLine("A.F"); }
ExpandedBlockEnd.gif}

None.gif
class  B: A
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif   
public override void F() dot.gif{ Console.WriteLine("B.F"); }
ExpandedBlockEnd.gif}

None.gif
class  C: B
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif   
new public virtual void F() dot.gif{ Console.WriteLine("C.F"); }
ExpandedBlockEnd.gif}

None.gif
class  D: C
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif   
public override void F() dot.gif{ Console.WriteLine("D.F"); }
ExpandedBlockEnd.gif}

None.gif
class  Test
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
ExpandedSubBlockStart.gifContractedSubBlock.gif   
static void Main() dot.gif{
InBlock.gif      D d 
= new D();
InBlock.gif      A a 
= d;
InBlock.gif      B b 
= d;
InBlock.gif      C c 
= d;
InBlock.gif      a.F();
InBlock.gif      b.F();
InBlock.gif      c.F();
InBlock.gif      d.F();
ExpandedSubBlockEnd.gif   }

ExpandedBlockEnd.gif}

None.gif

C 类和 D 类均含有两个具有相同签名的虚拟方法:A 引入的虚拟方法和 C 引入的虚拟方法。但是,由 C 引入的方法隐藏了从 A 继承的方法。因此,D 中的重写声明所重写的是由 C 引入的方法,D 不可能重写由 A 引入的方法。此例产生输出:

 
 
None.gif B.F
None.gifB.F
None.gifD.F
None.gifD.F

请注意,通过访问 D 的实例(借助一个派生程度较小的类型,它的方法没有被隐藏起来),可以调用被隐藏的虚拟方法。

引用 http://msdn.microsoft.com/library/chs/default.asp?url=/library/CHS/csspec/html/vclrfcsharpspec_10_5_4.asp

转载于:https://www.cnblogs.com/happlyonline/archive/2007/07/12/815252.html

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值