I would write this question directly to Jeffrey Richter, but last time he didn't answer me :) so I will try to get an answer with your help here, guys :)
In the book "CLR via C#", 3rd edition, on p.108, Jeffrey writes:
void M3() {
Employee e;
e = new Manager();
year = e.GetYearsEmployed();
...
}
The next line of code in M3 calls Employee’s nonvirtual instance GetYearsEmployed method. 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. In this case, the variable e is defined as an Employee. (If the Employee type didn’t define the method being called, the JIT compiler walks down the class hierarchy toward Object looking for this method. It can do this because each type object has a field in it that refers to its base type; this information is not shown in the figures.) Then, the JIT compiler locates the entry in the type object’s method table that refers to the method being called, JITs the method (if necessary), and then calls the JITted code.
When I read this first time I thought that it would be not effective to walk along the class hierarchy looking for the method during JIT-ting. It is easy to find the method already on compile stage. But I believed to Jeffrey. I posted this information on another forum and another guy confirmed my doubts that it is strange and would be ineffective and that it seems it is wrong information.
And really, if you look for the corresponding IL code in a decompiler, such as ILDasm or Reflector (I've checked in both) you will see that IL has a callvirt instruction calling the method from the base class, so JIT doesn't need to look in which class the method is located at runtime:
public class EmployeeBase
{
public int GetYearsEmployed() { return 1; }
}
public class Employee : EmployeeBase
{
public void SomeOtherMethod() { }
}
public class Manager : Employee
{
public void GenProgressReport() { }
}
...
Employee e;
e = new Manager();
int years = e.GetYearsEmployed();
Resulting IL is:
L_0000: nop
L_0001: newobj instance void TestProj.Form1/Manager::.ctor()
L_0006: stloc.0
L_0007: ldloc.0
L_0008: callvirt instance int32 TestProj.Form1/EmployeeBase::GetYearsEmployed()
You see? Compiler already found out that the method is located not in the Employee class, but in the EmployeeBase class and emited a right call. But from Richter's words JIT would have to find out that the method is actually located in the EmployeeBase class at runtime.
Did Jeffrey Richter mistaken? Or I don't understand something?
You cannot override a non-virtual or static method. The overridden base method must be virtual , abstract , or override . An override declaration cannot change the accessibility of the virtual method.
The non-virtual interface pattern (NVI) controls how methods in a base class are overridden. Such methods may be called by clients and overridable methods with core functionality. It is a pattern that is strongly related to the template method pattern.
No you cannot do that, the purpose of virtual methods is that derived classes can override the implementation and that the implementation is used even when called from base classes. If that causes problems then the code you need to run should not be in a virtual method.
The C# compiler resolves non-virtual methods exactly with no wiggle room. If a derived non-virtual method with the same signature appearch after the caller was compiled, the CLR will still call the "fixed" method the C# compiler chose. This is to avoid the brittle base class problem.
If you want dynamic method resolution, use virtual
. If you don't use virtual
you get fully static resolution. Your choice. The runtime type of the object reference becoming the this
pointer does not matter in resolution of non-virtual methods at all (neither for csc.exe not for the CLR JIT).
The JIT will always call the exactly chosen method. It will throw an exception if the method does not exist (maybe because the callee DLL was changed). It will not call a different method.
callvirt
can also call non-virtual methods. It is used to perform a null check. It is defined that way, and C# is defined to perform a null check on every call.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With