How c# differentiates between instance methods and virtual methods
Asked Answered
C

1

5

So basically I want to understand what are the general steps C# compiler takes to determine whether the method that is being called is nonvirtual instance method or virtual method.

Confusion comes from these 2 explanations (CLR via C# 3rd edition, Jeffrey Richter, Chapter 4 Type Fundamentals)

When calling a nonvirtual instance method, the JIT compiler locates the type object that corresponds to the type of the variable being used to make the call

And for virtual method calls

When calling a virtual instance method, the JIT compiler produces some additional code in the method, which will be executed each time the method is invoked. This code will first look in the variable being used to make the call and then follow the address to the calling object

I created small test project

class Program
    {
        static void Main(string[] args)
        {
            Parent p = new Derived();
            p.Foo(10); // Outputs Derived.Foo(int x)

            Derived d = new Derived();
            d.Foo(10); // Outputs Derived.Foo(double y)
        }
    }

    internal class Parent
    {
        public virtual void Foo(int x)
        {
            Console.WriteLine("Parent.Foo(int x");
        }
    }

    internal class Derived: Parent
    {
        public override void Foo(int x)
        {
            Console.WriteLine("Derived.Foo(int x)");
        }

        public void Foo(double y)
        {
            Console.WriteLine("Derived.Foo(double y)");
        }
    }

While Jon Skeet has a blog post explaining why program produces those outputs, and Eric Lippert confirms that in his blog post (check up the comments section), I still can't figure out how compiler decides whether the method that is being called is nonvirtual instance method or virtual method.

It seems that for nonvirtual instance method calls, compiler checks the type of variable being used to call a method, and for virtual methods - type of object that is referenced by variable that is being used to make a call to the method, so I guess, there should be some way to determine whether method is nonvirtual or virtual before deciding how to execute the method.

Compensable answered 31/5, 2013 at 20:57 Comment(6)
"determine whether method is nonvirtual or virtual" -- virtual methods are declared as virtual/override, non-virtual methods are not?Phosphorescent
so I guess, there should be some way to determine ... - Of course there is. The compiler knows everything about the types it works with.Naima
@Phosphorescent yes, but d.Foo() doesn't say anything about virtual/override.Compensable
@HenkHolterman sounds great, so compiler looks at d variables type or it looks at what object it references?Compensable
It looks at both types - that of the reference (the variable) and of the instance. And then it applies the rules.Naima
Think about it this way. The compiler gets the same source code that you are reading. Can you determine whether a method call refers to a virtual, instance or static method? Can you write a program that does the same thing? Then you can write that part of the compiler. How precisely the compiler does it is of course an implementation detail.Fourchette
H
10

there should be some way to determine whether method is nonvirtual or virtual before deciding how to execute the method.

The JIT compiler is not the same as the C# compiler. By the time execution reaches the JIT compiler, the C# compiler has already decided whether or not to emit a virtual method invocation, or a non-virtual method invocation. The only thing that the Richter book is telling you is the different things the JIT compiler does to invoke virtual vs. non-virtual methods. It is not telling you how the C# compiler decides whether or not to emit virtual or non-virtual method invocations.

In general, for that, you need to look at the language specification. If the method the compiler interprets the program code as invoking is virtual, it will emit a virtual call. Otherwise, it will not. The rules of the language in this particular case, and that's the point of Jon's post, dictate the compiler emit a non-virtual method call to Derived.Foo(double) in the second invocation. This is because in this case, the compiler interprets the program code (based again on the language specification) to be invoking Derived.Foo(double). The JIT has no idea about any of that, it merely sees IL to execute a non-virtual invocation to Derived.Foo(double) with the reference d as the implicit this parameter and 10 as the first explicit parameter.

Hearth answered 31/5, 2013 at 21:6 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.