在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
前言这篇文章出自我个人对C#虚函数特性的研究和理解,未参考、查阅第三方资料,因此很可能存在谬误之处。我在这里只是为了将我的理解呈现给大家,也希望大家在看到我犯了错误后告诉我。 用词约定
理论部分在父类与子类里,除了类之间的继承链,还存在方法之间的继承链。 C#里,在一个类中声明一个方法时,有四个和方法的继承性有关的关键字:new、virtual、sealed、override。
以及:
①其:指代正在进行声明的方法。
依照上述的说明,在调用类上的某个方法时,可以为该方法构建出一个或多个“方法继承链”。首先列出从子类②一直到父类③的类继承链,并列出这些类对该方法的最初定义或重定义。然后从父类到子类,逐个检查每个类对该方法的定义,按以下规则构造方法继承链:
当你拿到一个子类②的实例,却使用父类③的对象引用调用一个方法时(例如“A instanceRef = new C(); instanceRef.Foo1()”,这时类型A的引用就指向了类型C的对象),C#会先检查该方法是否为一个虚方法(使用了 virtual 关键字):如果不是,则简单地调用该方法的父类③版本即可;如果是,则沿着方法的继承链向下寻找,找到位于继承链底部的那个方法。 实践部分我定义了以下四个类:
当运行如下代码时,会打印出什么?
结果是:
例子很简单,依照之前的规则,可以画出如下一幅图。图中圆形的末端表示封闭、中断继承链;菱形的末端表示开放、允许构建继承链;类描述中的等式,表示从该类型的对象引用调用对应方法(等号左边的斜体)时,实际执行的代码体是在何处(等号右边的正常字体)定义的。 其实,为了确认这里描述出来的方法的继承链,甚至都不需要实地运行此代码。将代码放在Visual Studio里,使用“重构”(Refactor)菜单中的“重命名”(Rename)修改方法名称,待完成后就会发现在方法继承链的中断处,自动修改符号名称的动作也中止了。 补充对于 this 关键字,上述的规则也适用。只需要将 this 依照当前的代码上下文翻译为对应的类型引用,就可以依照之前叙述的方法确定最终调用的代码了。例如在C中的Foo1方法里假如有这么一条语句:“this.Foo2()”。当在外部运行“D.Foo2()”时,就会就会解析到“C.Foo1()”,这时,C.Foo1()方法的内部在解析“this.Foo2()”时就会解析到D.Foo2()。 对于 base 关键字,则比较简单,只是在基类的方法(这里“基类的方法”一词,请参见“用词约定”的第6条。)中找到同名方法,然后调用,不存在解析虚函数的过程。 对于被委托对象包装的方法指针,在调用委托时,仍会按照上述规则解析到正确的方法。 本文示例中使用了“Foo”开头的方法名,而这个习惯借鉴自一些别的文章。这里是有个典故还是怎么? |
请发表评论