Document directory
- Introduction:
- 1. Define a generic class
-
- 2. Define Constraints
- Reference
Introduction:
Since the birth of generics, the love of many object-oriented programmers is generally higher than that of other data structures. :). However, the support of various object-oriented programmers is not the same. In CPP, static compilation is used, that is, the compiler determines the generic type, while. Net determines at runtime. What are their differences?
I talked with my colleagues about generics and found that everyone was familiar with this and thought it was necessary to make an application summary. Because there are many fan content, please refer to the following:
Let's start with C # defining a piece of basic fan code.
1: public class GenericType<T> : IComparable<T>, IComparable<GenericType<T>>
2: where T: IComparable
3: {
4: public T Value;
5:
6: public int CompareTo(T t)
7: {
8: return this.Value.CompareTo(t);
9: }
10:
11: public int CompareTo(GenericType<T> t)
12: {
13: return this.Value.CompareTo(t.Value);
14: }
15: }
Decompiled IL code
1: .class public auto ansi beforefieldinit GenericType`1<([mscorlib]System.IComparable) T>
2: extends [mscorlib]System.Object
3: implements class [mscorlib]System.IComparable`1<!T>,
4: class [mscorlib]System.IComparable`1<class GenericType`1<!T>>
5: {
6: .field public !T Value
7: .method public hidebysig newslot virtual final
8: instance int32 CompareTo(!T t) cil managed
9: {
10: // Code size 24 (0x18)
11: .maxstack 8
12: IL_0000: ldarg.0
13: IL_0001: ldflda !0 class GenericType`1<!T>::Value
14: IL_0006: ldarg.1
15: IL_0007: box !T
16: IL_000c: constrained. !T
17: IL_0012: callvirt instance int32 [mscorlib]System.IComparable::CompareTo(object)
18: IL_0017: ret
19: } // end of method GenericType`1::CompareTo
20:
21: .method public hidebysig newslot virtual final
22: instance int32 CompareTo(class GenericType`1<!T> t) cil managed
23: {
24: // Code size 29 (0x1d)
25: .maxstack 8
26: IL_0000: ldarg.0
27: IL_0001: ldflda !0 class GenericType`1<!T>::Value
28: IL_0006: ldarg.1
29: IL_0007: ldfld !0 class GenericType`1<!T>::Value
30: IL_000c: box !T
31: IL_0011: constrained. !T
32: IL_0017: callvirt instance int32 [mscorlib]System.IComparable::CompareTo(object)
33: IL_001c: ret
34: } // end of method GenericType`1::CompareTo
35:
36: .method public hidebysig specialname rtspecialname
37: instance void .ctor() cil managed
38: {
39: // Code size 7 (0x7)
40: .maxstack 8
41: IL_0000: ldarg.0
42: IL_0001: call instance void [mscorlib]System.Object::.ctor()
43: IL_0006: ret
44: } // end of method GenericType`1::.ctor
45:
46: } // end of class GenericType`1
We split it into several simple parts for discussion.
1. Define a generic class
This part can be split
1: .class public GenericType <([mscorlib]System.IComparable) T>
2: extends [mscorlib]System.Object
3: implements class [mscorlib]System.IComparable<!T>
4: class [mscorlib]System.IComparable<class GenericType<T>>
5: {
6: }
Unlike declaring a non-generic class
1: <([mscorlib]System.IComparable) T>
Declare Variables 1: .class public value Pair<T>
2: {
3: .field public !T x
4: .field public !T y // fields x and y have the same type T
5: }
Declaration Method 1: .class public List`1<T>
2: {
3: .method public void Append(!T val) { ... }
4: .method public !T GetLast() { ... }
5: //...
6: }
Hide and overwrite
In this section, the implementation of the generic type is consistent with that of the Non-generic type. We can view an instance
We define two methods for the previously defined GenericType.
1: public void BaseRun()
2: {
3: Console.WriteLine("This is the base Run");
4: }
5:
6: public virtual void ParentMethod()
7: {
8: Console.WriteLine("The is the parentVirtual Method");
9: }
Define a base class DerivedClass for him.
1: public class DerivedClass<T> : GenericType<T>
2: where T : IComparable
3: {
4: public new void BaseRun()
5: {
6: Console.WriteLine("This is the derived class running");
7: }
8:
9: public override void ParentMethod()
10: {
11: Console.WriteLine("This is the derived class");
12: }
13: }
Compare the generated IL code
1: .method public hidebysig instance void BaseRun() cil managed
2: {
3: // Code size 11 (0xb)
4: .maxstack 8
5: IL_0000: ldstr "This is the derived class running"
6: IL_0005: call void [mscorlib]System.Console::WriteLine(string)
7: IL_000a: ret
8: } // end of method DerivedClass`1::BaseRun
9:
10: .method public hidebysig virtual instance void
11: ParentMethod() cil managed
12: {
13: // Code size 11 (0xb)
14: .maxstack 8
15: IL_0000: ldstr "This is the derived class"
16: IL_0005: call void [mscorlib]System.Console::WriteLine(string)
17: IL_000a: ret
18: } // end of method DerivedClass`1::ParentMethod
2. Define Constraints
Let's use IL to explain the constraints mentioned above.
- Value Type Constraints
1: .class public ClassType`1<valuetype .ctor ([mscorlib]System.ValueType) T>
2: extends [mscorlib]System.Object
3: {}
- Reference Type Constraints
1: .class public ClassType`1<class T> extends [mscorlib]System.Object
- Interface Type Constraints
- Refer to the code above this article
- Base Constraint
1: .class public BaseClass`1<(class GenericType`1<!T>, [mscorlib]System.IComparable) T>
2: extends [mscorlib]System.Object
- No parameter constructor Constraints
1: .class public auto ansi beforefieldinit CtorType`1<.ctor T>
2: extends [mscorlib]System.Object
- Multiple generic type relationship Constraints
1: .class public ParentChild`2<T, (!T)U> // U must bedescendant of T
2: {
3: //TODO:: Implement this ...
4: }
- (! T) U indicates that U must be a subclass that can be converted to T by type conversion, that is, U is T.
- Come to a complex point
1: .class public auto ansi beforefieldinit ComplexType`1<class .ctor ([mscorlib]System.IComparable) T>
2: extends [mscorlib]System.Object
We can see that the differences between several types only exist in <type definition>
Here, I still don't understand. Although the things at the IL level are quite concise, how are the advantages of generics realized? It's no surprise to see the definition of metadata.
Metadata definition:
Generic Param (Generic parameter), that is, <type definition>:
GenericParam Metadata Table:
Name |
Function |
Number (2-byte unsigned integer ). |
Location of generic parameters in a generic type |
Flags (2-byte bit field ). |
Generic Constraints |
Owner (coded token of type TypeOrMethodDef ). |
Generic parameters |
Name (offset in the # Strings stream ). |
Generic parameter name |
Generic Method (Generic Method ):
1: <gen_method_def> ::=
2: .method <flags> <call_conv> <ret_type> <name>< <gen_params> > (<arg_list>) <impl>
3: { <method_body> }
4: For example:
5: .method public static !!T GetMedian<T>(!!T[] tarray)
6: {
7: ldarg.0
8: dup
9: ldlen
10: ldc.i4.1
11: shr
12: ldelem !!T
13: ret
14: }
About CLR, it seems difficult to find relevant information during runtime creation, which needs to be further explored. :) this article has been written intermittently for a long time. First, draw a full stop.
Reference
- Expert IL runner er 2.0
- Professional. Net 2.0 Generic
- ECMA Partion II