C# emits “callvirt” for non-virtual method
Tags: programming, benchmark, oop, dotnet
Published: 2019-05-22 15:19:59

---
title: C# emits “callvirt” for non-virtual method
published: true
tags: programming,benchmark,oop,dotnet
canonical_url: https://medium.com/@joni2nja/c-emits-callvirt-for-non-virtual-method-1b3d8f58ea79
---
This post originally appeared on [Medium](https://medium.com/@joni2nja/c-emits-callvirt-for-non-virtual-method-1b3d8f58ea79)

---

#### C# almost always emit “callvirt” for non-virtual method except…

Let’s dive into IL world a little bit.

First, take a look at the following super simple C# code running on [LINQPad](https://www.linqpad.net/):

![](https://cdn-images-1.medium.com/max/342/1*1zooSleYEpn67IUptN_6aw.png)

We can see the generated IL from the IL Results Panel, also shown in the above screenshot. Let’s just pay attention to the highlighted parts.

Okay, so `new Coba().Say()` will translates to IL call.

Now, let’s add `virtual` to the method. Like so:

![](https://cdn-images-1.medium.com/max/374/1*96YASoiJD4iONgbRilFTWg.png)

Oh, now the IL generates `callvirt` instead of `call`, which makes sense.

> The `callvirt` instruction calls a late-bound method on an object. That is, the method is chosen based on the runtime type of `obj` rather than the compile-time class visible in the method pointer. `Callvirt` can be used to call both virtual and instance methods.

Here is the official documentation:

[OpCodes.Callvirt Field (System.Reflection.Emit)](https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.callvirt?view=netframework-4.7.2)

And `call`:

> The call instruction calls the method indicated by the method descriptor passed with the instruction. The method descriptor is a metadata token that indicates the method to call and the number, type, and order of the arguments that have been placed on the stack to be passed to that method as well as the calling convention to be used.

[OpCodes.Call Field (System.Reflection.Emit)](https://docs.microsoft.com/en-us/dotnet/api/system.reflection.emit.opcodes.call?view=netframework-4.7.2)

Now, let’s change the code a little bit, saving the object to an instance variable.

![](https://cdn-images-1.medium.com/max/380/1*1cgx-3yZ-Vj4S75f8AyPuw.png)

Uh oh, wait, the generated IL is not `call` but `callvirt` …  
Well, actually it’s by [design](https://blogs.msdn.microsoft.com/ericgu/2008/07/02/why-does-c-always-use-callvirt/) since .NET v1.

[Why does C# always use callvirt?](https://blogs.msdn.microsoft.com/ericgu/2008/07/02/why-does-c-always-use-callvirt/)

C# has evolved since then. C# now has `?.` null-conditional operator. Let’s change the code again to use this operator.

![](https://cdn-images-1.medium.com/max/358/1*sZ8DmzVppu7jmYEWnFf4Tg.png)

Okay, this way the compiler can be sure that the object won’t be null so it can optimize it to use `call` instead of `callvirt`.

Now, let’s add virtual.

![](https://cdn-images-1.medium.com/max/371/1*DdL1KRSp0NhTwPfaJFqAVA.png)

This time, `callvirt` is being used, as expected.

### Benchmarks

How about benchmarking `callvirt` vs `call`?  
We need to make sure we really compare `callvirt` and `call`. Let’s make a diff of the last two codes above. Here is the output:

![](https://cdn-images-1.medium.com/max/1024/1*zNbNdPaNo8pXuctO5cNoLg.png)

We are good to go.  
Here is the code of the benchmark.

![](https://cdn-images-1.medium.com/max/1024/1*LMG_95Mf_-Omyss85UYEiw.png)

And the result:

![](https://cdn-images-1.medium.com/max/1024/1*rsoyab3720GgfwUTN0Xpig.png)

The result tells us that the performance impact is so small.

### Summary

C# almost always emit a `callvirt` when calling a method, even on a non-virtual method. `callvirt` has the advantage of doing an implicit null-check, so that the `NullReferenceException` will be thrown when you call a method on a null object. If the compiler ‘knows’ that the object won’t be null, it can optimize away to use `call` instead. The performance impact is so small that we should not really care about it.