Why is the C# compiler emitting a callvirt instruction for a GetType() method call?

微笑、不失礼 提交于 2019-11-27 04:13:19

Just playing safe.

Technically C# compiler doesn't always use callvirt

For static methods & methods defined on value types, it uses call. The majority is provided via the callvirt IL instruction.

The difference that swung the vote between the two is the fact that call assumes the "object being used to make the call" is not null. callvirt on the other hand checks for not null and throws a NullReferenceException if required.

  • For static methods, the object is a type object and cannot be null. Ditto for value types. Hence call is used for them - better performance.
  • For the others, the language designers decided to go with callvirt so the JIT compiler verifies that the object being used to make the call is not null. Even for non-virtual instance methods.. they valued safety over performance.

See Also: Jeff Richter does a better job at this - in his 'Designing Types' chapter in CLR via C# 2nd Ed

See this old blog post by Eric Gunnerson.

Here's the text of the post:

Why does C# always use callvirt?

This question came up on an internal C# alias, and I thought the answer would be of general interest. That's assuming that the answer is correct - it's been quite a while.

The .NET IL language provides both a call and callvirt instruction, with the callvirt being used to call virtual functions. But if you look through the code that C# generates, you will see that it generates a "callvirt" even in cases where there is no virtual function involved. Why does it do that?

I went back through the language design notes that I have, and they state quite clearly that we decided to use callvirt on 12/13/1999. Unfortunately, they don't capture our rationale for doing that, so I'm going to have to go from my memory.

We had gotten a report from somebody (likely one of the .NET groups using C# (thought it wasn't yet named C# at that time)) who had written code that called a method on a null pointer, but they didn’t get an exception because the method didn’t access any fields (ie “this” was null, but nothing in the method used it). That method then called another method which did use the this point and threw an exception, and a bit of head-scratching ensued. After they figured it out, they sent us a note about it.

We thought that being able to call a method on a null instance was a bit weird. Peter Golde did some testing to see what the perf impact was of always using callvirt, and it was small enough that we decided to make the change.

Marc Gravell

As a (perhaps-)interesting aside... GetType() is unusual in that it isn't virtual - this leads to some very, very odd things.

(marked as wiki as it is somewhat off-topic to the actual question)

The compiler doesn't know the real type of o in the first expression, but it does know the real type in the second expression. It looks like it's only looking at one statement at a time.

This is fine, because C# depends heavily on the JIT for optimization. It's very likely in such a simple case that both calls will become instance calls at runtime.

I don't believe callvirt is ever emitted for non-virtual methods, but even if it was, it would be no problem because the method would never be overridden (for obvious reasons).

I would hazard a guess that it's because the first assigns to a variable, which potentially could contain a downcasted instance of another type that could've overridden GetType (even though we can see it doesn't); the second could never be anything other than Object.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!