Parse the essence of inheritance in. net

Source: Internet
Author: User
In csdn forums and blogs, there are many posts discussing the inheritance syntax in C #. Everyone is happy to explain the inheritance in virtual, override, new, final, interface, and class. A variety of examples make the novice dizzy, and some of them are just to be passed. For example, this article says, "the compiler will find the appropriate method body along the inheritance chain ", in the response, another person said, "This feature features C # Compiler's support for the Lee replacement principle. That is, subclasses of any base class must be applicable. For example, class base {}, class sub: Base {} If: Base A = new sub (); then the compiler can determine the type of the object to which a points. Therefore, the method address is determined during the compilation period .". I am speechless, and the implementation of override is essentially very simple, but every time I discuss the problem from the perspective of syntax, it is always impossible to get things done. Therefore, this post will explain the implementation of override to you from the underlying implementation perspective. First, let's clarify two concepts:
1. Call methods of a class instance are all virtual calls (callvirt) and only real calls are called when the base class methods are called ). See the Code:

1. Class
2 .{
3. Public void BOO ()
4 .{
5. Console. writeline ("A: Boo ().");
6 .}
7. Public Virtual void Foo ()
8 .{
9. Console. writeline ("A: Foo ().");
10 .}
11 .}
12. Class B:
13 .{
14. Public override void Foo ()
15 .{
16. Console. writeline ("B: Foo ().");
17. Base. Foo ();
18 .}
19 .}
20. Class class1
21 .{
22. Public static void main ()
23 .{
24. A = new ();
25. A. Foo ();
26. A. Boo ();
27. a B = new B ();
28. A. Foo ();
29. A. Boo ();
30 .}
31 .}

Let me see the corresponding il code snippet.

1. // main function, call the Il of the Instance Function
2. // A. Foo ();
3. il_0007: callvirt instance void test_console.a: Foo ()
4. // A. Boo ();
5. il_000d: callvirt instance void test_console.a: Boo ()
6. // B. Foo ();
7. il_0019: callvirt instance void test_console.a: Foo ()
8. // B. Boo ();
9. il_001f: callvirt instance void test_console.a: Boo ()
10.

11. // Foo function in Class B
12. il_0000: ldstr "B: Foo ()."
13. il_0005: Call void [mscorlib] system. Console: writeline (string)
14. il_000a: ldarg.0
15. // base. Foo ();
16. il_000b: Call instance void test_console.a: Foo ()
17. il_0010: Ret

From the above Il, we can clearly understand that the callvirt command is used to call virtual and non-virtual functions for the referenced object, the call command is used only when the base class functions are called (to prevent infinite recursive calls ). We can also see that the function signature after callvirt is only related to the declared type rather than the actual object type (for a B = new B (); the compiler only cares about the previous type A and does not care about the subsequent type B ).
2. the first 4 bytes of reference type instance content on the managed stack point to this type of method table (methodtable ), the Value Type instance does not contain the method table address (for more information about the value type, see here ). Next, I will use SOS. DLL to view the content in the method table (for details about SOS. dll and method table, refer to the blog and msdn ):
(1) obtain the address of object A and object B, and view the content of the GC (through the clrstack-a command)
<CLR Reg> = 0x012e1d68 //
009730e8 00000000 // The first 4 bytes are methodtable
<CLR Reg> = 0x012f93fc // B
00973188 00000000
(2) view the content of the method table (using the dumpmt-MD command)
------------------- A type method table content ---------------------
79354bec 7913bd48 prejit system. Object. tostring ()
793539c0 7913bd50 prejit system. Object. Equals (system. Object)
793539b0 7913bd68 prejit system. Object. gethashcode ()
7934a4c0 7913bd70 prejit system. Object. Finalize ()
00973148 009730d8 JIT test_console.a.foo () // virtual method foo
00973138 009730d0 JIT test_lele.a.boo ()
00973158 009730e0 JIT test_console.a .. ctor ()
------------------- B type method table content ---------------------
79354bec 7913bd48 prejit system. Object. tostring ()
793539c0 7913bd50 prejit system. Object. Equals (system. Object)
793539b0 7913bd68 prejit system. Object. gethashcode ()
7934a4c0 7913bd70 prejit system. Object. Finalize ()
009731d0 00973178 none test_console. B .foo () // Foo overwritten by B
009731e0 00973180 JIT test_console. B .. ctor ()
We found that the virtual method foo is placed at the 5th position in both A and B, but because B overwrites Foo, the original. foo overwrites B. foo. Whether you have understood this, the so-called override is simply replacing the address of the parent class virtual method in the method table of the subclass with the address of the corresponding override method of the subclass. This concept is called virtual assignment. So every time you call a: Foo (), you only need to get the actual address of foo. If the subclass overwrites Foo, the address is of the subclass Foo, if not, the foo address in the parent class is used. If the subclass uses the new or new virutal method to write Foo, the method table records the address of B. Foo under all the Virtual Methods of. If you have not yet understood it, let's look at the disassembly to verify this conclusion:

1. A. Foo ();
2. 10000003b mov ECx, EDI // ESI is the object address
3. 10000003d mov eax, dword ptr [ECx] // [ECx] Get the method table address because the first 4 bytes are the method table address.
4. 0000003f call dword ptr [eax + 38 H] // call the method at the 0x38 offset
00000042 NOP
6.
7. B. Foo ();
8. 00000062 mov ECx, EBX
9. 00000064 mov eax, dword ptr [ECx]
10. 00000066 call dword ptr [eax + 38 H] // call the method at the offset 0x38
11. 00000069 NOP
12.

Therefore, for the compiler, it does not know any inheritance or virtual functions, but the call address of the function is obtained for each execution of the compiled code by JIT when calling the virtual function by the Il callvirt command. So why do call non-virtual functions also use the callvirt command? Let's take a look at the Assembly commands used to call the boo method.

1. A. Boo ();
2. 00000043 mov ECx, EDI
3. 00000045 cmp dword ptr [ECx], ECx
4. 00000047 call ff9430c8
5. 10000004c NOP
6. B. Boo ();
7. 0000006a mov ECx, EBX
8. 2017006c cmp dword ptr [ECx], ECx
9. 0000006e call ff9430c8
10. 00000073 NOP

Dword ptr [ECx] and ECx are used to check whether the this pointer is null, while the subsequent call ff9430c8 command directly calls the boo method. Therefore, when callvirt calls a non-virtual method, It just checks it. In fact, it still uses the call command to directly call the method (static function call method ). At this time, you should also understand why the call command should be used to call the base class function (base. Foo.
So many of them are about the class inheritance method, and the implementation method for the interface is slightly different. CLR puts the implementation (implicit, display) addresses of each class to a global interface map table ), then, when the interface is called, query this table to locate the specific called functions. This method is used to distinguish between interface calls and class calls. Someone raised this question on the Forum:

1. interface IC
2 .{
3. Void test ();
4 .}
5. Class C: IC
6 .{
7. Public void test ()
8 .{
9. Console. writeline ("C: Test ().");
10 .}
11 .}
12. Class D: c
13 .{
14. Void test ()
15 .{
16. Console. writeline ("D: Test ().");
17 .}
18 .}
19.

20. IC Ic = new D ();
21. IC. Test (); // Why is the result C: Test ().

If we look at Il, we will find that although the test in C is a virtual function, it is only final.
. Method public hidebysig newslot virtual final instance void test () cel managed
Therefore, although D inherits from C, test in B is new from the perspective of class inheritance. If we want d to inherit from the IC interface or change the test method in C to virtual, in D, the test method will be added to the global interface ing table. The method for calling the interface method after JIT in. net2.0 has changed, so the details of interface inheritance have not been fully understood. If you know, please let me know.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.