Emb*_*rja 0 c# virtual-functions
我看到当我调用类的实例方法时,C#编译器会发出callvirt调用该方法的
指令,为什么会这样呢?
这是否意味着所有实例方法都被virtual methods编译器视为什么,这是什么谜?
Han*_*ant 12
它是在那里实现C#语言规范中的承诺.其中说通过null引用调用类的实例方法是不合法的.这可能听起来像一个明显的特征,但它实际上并不常见于OOP语言.特别是C++/CLI编译器没有它.CLI规范没有它.像C++这样的非托管语言没有它.
有时候,当实例方法不使用任何非静态类成员时,它甚至会达到一个好的结果.这种方法当然应该是静态的,但不是必需的或强制执行的.
C#要求非常好,它使NullReferenceException诊断更容易.由于它们是在调用站点而不是在实例方法内生成的,因此它阐明了对象引用为空.弄清楚方法中的这个引用是空的有点困难,特别是因为你看不到它.进一步复杂的地址实际上不是null,访问类的字段将生成一个偏离0的地址.如果对象是巨大的,超过64千字节则反过来是不安全的.在这样一个大对象的末尾访问一个字段不一定会产生处理器异常,你只需要阅读随机垃圾.如果你写它就会损坏内存.
所以C#团队寻找一种廉价的方法来实现null测试.并在callvirtIL指令中找到了一个.与此不同call,它确实在CLI规范中承诺例外.这是一个非常便宜的测试,它只需要一个机器代码指令.并且不需要分支,如果处理器的分支预测逻辑猜错了那就非常昂贵.
你现在也知道为什么String.Equals()包含这个神秘的代码:
public override bool Equals(Object obj) {
if (this == null)
throw new NullReferenceException();
// etc...
}
Run Code Online (Sandbox Code Playgroud)
Eri*_*ert 12
汉斯和迈克的答案是正确的.只是添加一些额外的信息:
该callvirt指令在非虚拟方法上被明确记录为合法,以便您获得空检查行为.
在极少数情况下,C#编译器证明非虚拟调用不可能具有空接收器,它会回退call并保存进行空检查所需的纳秒.例如,如果你有(new C()).InstanceMethod()那么应该生成call,而不是callvirt因为编译器知道接收表达式永远不是null.(如果分配失败,则抛出异常,因此永远不会执行调用.)
| 归档时间: |
|
| 查看次数: |
1464 次 |
| 最近记录: |