|
Generic internal mechanism
Generics have type parameters, which provide a "parameterized" type via type parameters. In fact, the "type parameter" of the generic type becomes a generic type of metadata, and the "runtime" takes advantage of them when needed Constructing the appropriate types, through which we can instantiate different types of objects. That is, an unbound generic type is a blueprint that constructs a generic type, a generic type, and an actual object.
Analyze generic IL code
Consider an example in which a generic class for comparison and a non-generic class for comparison int are defined in this example:
Namespace GenericTest
{
Class CompareUtil < T> where T: IComparable
{
Public T ItemOne {get; set;}
Public T ItemTwo {get; set;}
Public CompareUtil (T itemOne, T itemTwo)
{
This.ItemOne = itemOne;
This.ItemTwo = itemTwo;
}}
Public T GetBiggerOne ()
{
If (ItemOne.CompareTo (ItemTwo)> 0)
{
Return ItemOne;
}}
Return ItemTwo;
}}
}}
Class IntCompareUtil
{
Public int ItemOne {get; set;}
Public int ItemTwo {get; set;}
Public IntCompareUtil (int itemOne, int itemTwo)
{
This.ItemOne = itemOne;
This.ItemTwo = itemTwo;
}}
Public int GetBiggerOne ()
{
If (ItemOne.CompareTo (ItemTwo)> 0)
{
Return ItemOne;
}}
Return ItemTwo;
}}
}}
Class Program
{
Static void Main (string [] args)
{
CompareUtil < int> compareInt = new CompareUtil < int> (3, 6);
Int bigInt = compareInt.GetBiggerOne ();
IntCompareUtil intCompareUtil = new IntCompareUtil (4, 7);
Int big = intCompareUtil.GetBiggerOne ();
Console.Read ();
}}
}}
}}
First of all, ILSpy look at the generic class "CompareUtil < T>" IL code (only listed a part of the IL code)
.class private auto ansi beforefieldinit GenericTest.CompareUtil`1 < ([mscorlib] System.IComparable) T>
Extends [mscorlib] System.Object
{
...
.method public hidebysig specialname rtspecialname
Instance void .ctor (
! T itemOne,
! T itemTwo
) Cil managed
{...}
...
// Properties
.property instance! T ItemOne ()
{
.get instance! 0 GenericTest.CompareUtil`1 :: get_ItemOne ()
.set instance void GenericTest.CompareUtil`1 :: set_ItemOne (! 0)
}}
.property instance! T ItemTwo ()
{
.get instance! 0 GenericTest.CompareUtil`1 :: get_ItemTwo ()
.set instance void GenericTest.CompareUtil`1 :: set_ItemTwo (! 0)
}}
} // end of class GenericTest.CompareUtil`1
Copy the code
You can see that the IL code for a generic class is basically the same as the IL code for a non-generic class, except that there is some type parameter metadata in the IL class of the generic class.
Here's a look at some of the special points in the generic IL code:
GenericTest.CompareUtil`1 < ([mscorlib] System.IComparable) T>
`1 'represents the number of elements, which is the number of type parameters
< ([Mscorlib] System.IComparable) T> is the type constraint we add to the generic type
! T and! 0
! T is a placeholder for the type parameter
! 0 represents the first type parameter (when the generic meta-number is 2,! 1 represents the second type parameter)
At the same time, we can also compare the generic and non-generic class instance of IL code
IL_0003: newobj instance void class GenericTest.CompareUtil`1 < int32> ::. Ctor (! 0,! 0)
IL_0012: newobj instance void GenericTest.IntCompareUtil ::. Ctor (int32, int32)
Generic mechanism
According to the above analysis can be obtained, C # generic CLR at the run-time support capabilities, the compiler in dealing with the time to do two things:
When the compiler encounters a generic code such as "CompareUtil < T>", the compiler uses special placeholders to represent the type parameters when compiling generic code into IL code and metadata
While the actual generic instantiation work in the "on-demand" approach, that is when the compiler encountered "CompareUtil < int> compareInt" specified type parameters of the code, according to the type parameter JIT will be generic type IL translates to native code, which already uses the actual data type, equivalent to the class declared with the actual type
Instantiation of value types and reference types
The JIT generates the same native code for all generic types whose type is "reference type". This is done because all the references have the same size.
However, if the type parameter is a "value type", JIT will generate a separate native code for each of the different "value types".
As for why the use of generic types can be avoided value boxing and unboxing operation:
List < int> intList = new List < int> ();
I believe we see the following IL code to understand, in the generic category, are directly through the use of type parameters type.
// Fields
.field private! T [] _items
Use typeof for generic types
In C #, we often use typeof operator to get a System.Type object reference.
For generic types, we can also use typeof in two ways:
Get a generic type definition (unbound generic type)
In order to obtain the definition of generic types, only need to provide the type name of the statement, delete all types of parameters, but keep the comma
Gets a specific constructed type (that is, a type reference that gets a closed type)
You only need to specify a type argument
The following look at a simple example,
Static void DemonstrateTypeOf < T> ()
{
Console.WriteLine (typeof (T));
Console.WriteLine (typeof (List < >));
Console.WriteLine (typeof (Dictionary < ,>));
Console.WriteLine (typeof (List < T>));
Console.WriteLine (typeof (Dictionary < string, T>));
Console.WriteLine (typeof (List < long>));
Console.WriteLine (typeof (Dictionary < string, int>));
}}
The output of the function is as follows:
System.Double
System.Collections.Generic.List`1 [T]
System.Collections.Generic.Dictionary`2 [TKey, TValue]
System.Collections.Generic.List`1 [System.Double]
System.Collections.Generic.Dictionary`2 [System.String, System.Double]
System.Collections.Generic.List`1 [System.Int64]
System.Collections.Generic.Dictionary`2 [System.String, System.Int32]
From the output, we can also see the type of each generic meta, as well as the generic types (unbound generic types and enclosing types).
Static fields and static constructors
A static field in a generic
In C #, static member variables of a class are shared between different class instances, and can be accessed by class names. C # 2.0 introduced generics, resulting in static member variables of the mechanism there have been some changes: static member variables in the same closed type of sharing between different closed types are not shared. This is also very easy to understand, because different types of closure have the same class name, but because of the different data types were introduced, they are completely different types.
Look at a simple example:
Namespace GenericTest
{
Class TypeWithField < T>
{
Public static string field;
Public static void PrintField ()
{
Console.WriteLine (field);
}}
}}
Class Program
{
Static void Main (string [] args)
{
TypeWithField < int> .field = "Int Field";
TypeWithField < string> .field = "String Field";
TypeWithField < int> .PrintField ();
TypeWithField < string> .PrintField ();
Console.Read ();
}}
}}
}}
Static constructors in generics
Static constructor rules: only one, and can not have parameters, he can only be. NET runtime call automatically, but can not be manually called, and can only be executed once.
Generics of the static constructor of the principle and non-generic class is the same, just differentiate the generic type of closure can be understood as different classes. |
|
|
|