Search code examples
.net.net-3.5cil

.NET CIL Call or CallVirt?


How can I determine if a method needs to be called with "Call" or "Callvirt"?


Solution

  • You can follow these simple rules one by one to determine which you should use:

    • Is the method static? Then use call.
    • Is the type you are invoking on a value type? Then use call. (This does not apply if the value is boxed -- then you are actually invoking on object or some interface, and those are reference types.)
    • Is the method you are invoking declared virtual or abstract? Then use callvirt.
    • Are you invoking the method through an interface reference? Then use callvirt.
    • Is the method you are invoking declared override, but neither the method nor the declaring type declared sealed? Then use callvirt.

    In all other cases, no virtual dispatch is required so you can use call -- but you should use callvirt. Here's why:

    Using callvirt on non-virtual methods is equivalent to call except when the first argument is null. In that case callvirt will throw a NullReferenceException immediately, whereas call will not. This makes sense, because callvirt is intended to be used in cases where virtual method dispatch is desired, and you can't do virtual method dispatch if you don't have an object on which to do a vtable lookup.

    Note that callvirt will still throw an exception if the first argument is null even if a vtable lookup isn't necessary!

    Considering this information, using callvirt for all non-static method invocations on reference types (as the C# compiler does) may be preferable as it will cause a NullReferenceException immediately at the call site instead of sometime later when this gets used (explicitly or implicitly) inside of the method.