首先回答一个问题:is/as 关键字是通过CLR中的 isinst 指令实现,而非反射。
isinst 在当下CoreCLR实现中,对于引用以及值类型,JIT会生成代码调用CoreCLR内部的FCall方法 JIT_IsInstanceOfClass_PortableJIT_IsInstanceOfClass ,而对于接口类型的断定会调用JIT_IsInstanceOfInterface_PortableJIT_IsInstanceOfInterface 。
这里以 JIT_IsInstanceOfClass_Portable 为例:
HCIMPL2(Object*, JIT_IsInstanceOfClass_Portable, MethodTable* pTargetMT, Object* pObject) { FCALL_CONTRACT; //对传入对象引用判空 if (NULL == pObject) { return NULL; } //获取对象本身的MethodTable PTR_VOID pMT = pObject->GetMethodTable(); //遍历继承链,试图比较是否有相等的类型 do { if (pMT == pTargetMT) return pObject; pMT = MethodTable::GetParentMethodTableOrIndirection(pMT); } while (pMT); //判断是否有类型等效 if (!pObject->GetMethodTable()->HasTypeEquivalence()) { return NULL; } ENDFORBIDGC(); return HCCALL2(JITutil_IsInstanceOfAny, CORINFO_CLASS_HANDLE(pTargetMT), pObject); }
可以看到整个流程相当简单,核心就是获取对象的MethodTable在继承链上与目标MethodTable比较。一般来说只要继承链不是很长,都不会存在过多的Overhead。
当然其中另一个因素是FCall本身就是为托管代码对Runtime内部高性能方法调用所设计的:
综合上面两点,对 isinst 指令JIT生成的不过仅是简单的参数传递与一个call而已,Overhead不成大问题。
附:对代码
JIT为 isinst 指令生成的本机代码(x86)为
为了验证我们的猜想,进行实验对比:
对总是使用as
缓存as结果
10000次重复流程中,缓存as结果得到的时间消耗大概为3.4~3.7ms
而直接使用as不相上下,时间消耗大概为3.3~3.6ms
因此在最后我们可以得出结论,是否缓存as对运行时间的影响是无关痛痒的。
应评论要求用Stopwatch包裹住for循环,循环次数增加到一千万,总是使用as的时间消耗在40~50ms,而使用缓存策略则在100~110ms(带反转)就不放图了,懒。
本站所有内容均为互联网搜索引擎提供的公开搜索信息,本站不存储任何数据与内容,任何内容与数据均与本站无关,如有需要请联系相关搜索引擎包括但不限于百度,google,bing,sogou 等
© 2025 tinynews.org All Rights Reserved. 百科问答小站 版权所有