1. Generic
The important value of generics in. Net does not need to be described in many languages. Starting from the introduction of generic type in. net2.0, this item has been widely praised by developers, and it is quite insightful. As you can imagine,. Net was designed to implement this feature. Microsoft pinned this hope on system. object, but it proved afterwards that the performance overhead brought by the latter was unacceptable by developers. Therefore, the chicken ribs were abandoned. To solve this problem, Microsoft worked hard at. net2.0 and finally developed a perfect solution, generic. In fact, generic is a virtual "generic", and its principle is easy to understand, that is, during the first compilation, use a placeholder to replace the original position of the type, and then replace the placeholder with the type specified in the Code during the second compilation. I remember that when I was in college, I often sent only one person in the dormitory to grab a seat. If I put my books or bags in an empty position, it means that someone else can't sit in this position. One minute before the class, these talents ran to snatch ready-made seats with full eyes. The boss who probably came up with the generic model was inspired by the University (purely speculation ).
This process is represented in Code as follows:
Code in the source program:
Class human <t>
{
Public void say (T word)
{
Console. writeline (word. tostring ());
}
}
This generic class represents a human class. After the class name is human, <t> is used to indicate the introduction of generic. T is a placeholder. Here, it can represent any type. In the say method, t is used as the parameter type to receive different types of values.
After the first compilation, the above program's il code becomes as follows:
As shown in the figure, t is used as the type identifier for both the human class and the say method parameters. Of course, t is only the specification for installing Microsoft, here, you can use another character or word to represent the same effect. However, classes and methods do not know what type they are. The following code appears in the main method of the class:
At this point, we can clearly see through the smart sensing prompt that the parameter type after the say method is specified as string type. It indicates that the type of the generic class is string during initialization, And the placeholder T is replaced with string. In fact, this conversion occurs during secondary compilation, that is, during JIT compilation. From this we can know that the. NET generic is a virtual generic type, but uses a placeholder as the intermediary, so that the program achieves the generic effect. This is also a perfect solution.
By the way, you must know the implementation principle of generics in Mr. Wang Tao's article. in chapter 1 of the book "Net", I made it very clear that friends who are still not very clear about this concept can also study it well.
2. Implementation
Understanding the principles of generics, and then using generics is not that difficult. First, we need to know that the biggest benefit of generics is to solve the performance loss caused by type conversion and avoid various errors caused by this conversion. As a result, we understand that generic type is needed in scenarios where type conversion often occurs. In this case, data type conversion is often triggered, and the data set is used. The following is an example:
Using system;
Using system. collections;
Namespace consoleapplication5
{
Class Program
{
Static void main (string [] ARGs)
{
Arraylist Al = new arraylist ();
Al. Add ("ABC ");
ALS. Add (123 );
Al. Add ('A ');
Al. Add (true );
Foreach (string STR in Al)
{
Console. writeline (Str. tostring ());
}
}
}
}
In this example, arraylist of the Collection class is used. Due to the usage of nonstandard types, three packing operations occur when records are added to the set in this program, which greatly reduces the program performance. This is nothing. The key is that, when the foreach loop is used, the type conversion error occurs when reading records due to the different data types in the Collection class, however, this error cannot be detected during compilation because it is not safe to use the collection class.
Of course, we rarely use collection classes in this way in actual development, but a good programmer always hopes to eliminate all possible hidden dangers in the safest and safest way, such instability cannot be left in the program ,. net in the system. collections. the namespace of generic provides the implementation of generic collection classes.
Using system;
Using system. collections;
Using system. Collections. Generic;
Namespace consoleapplication5
{
Class Program
{
Static void main (string [] ARGs)
{
List <int> List = new list <int> ();
List. Add ("ABC"); // This error indicates that the Set Data is incorrectly added and the program cannot pass the compilation check.
List. Add (1, 123 );
}
}
}
After debugging or compiling this program, an error will be reported because the generic list set is used and the type is locked to the int type at the beginning of the definition. If the operation does not match when the set data is added, the program cannot pass the check. This avoids possible problems.
3. Generic Constraints
In this topic, by the way, we talk about the principle of development: "errors should be discovered as early as possible ". All possible errors should be killed in the cradle. in the form of a classic villain line, "Another one thousand error can be killed by mistake, but one cannot be missed". (Are programmers destined to become cold-blooded animals ?)
To follow this principle, we need to add constraints to generics. Constraints are further specifications for generic classes. Let's look at the example below:
Code
Using system;
Namespace consoleapplication1
{
Class Program
{
Static void main (string [] ARGs)
{
Sword = new sword ();
Hero <sword> hero = new hero <sword> ();
Console. writeline ("Current hero attack value: {0}:", hero. attackvalue );
// Attach tianjian to the hero
Hero. addarm (sword );
Console. writeline ("the hero's attack value is {0}:", hero. attackvalue after the weapon is equipped );
}
}
/// <Summary>
/// Item Interface
/// </Summary>
Interface iweapon
{
// Item Name
String weaponname
{Get ;}
// Item effect value
Int weaponvalue
{Get ;}
}
/// <Summary>
/// Yi tianjian class
/// </Summary>
Class sword: iweapon
{
Public sword ()
{
This. _ weaponname = "Yi tianjian ";
This. _ weaponvalue = 500;
}
Private string _ weaponname;
/// <Summary>
/// Name of the sword
/// </Summary>
Public String weaponname
{
Get {return _ weaponname ;}
}
Private int _ weaponvalue;
// Attack effect of the sword
Public int weaponvalue
{
Get {return _ weaponvalue ;}
}
}
/// <Summary>
/// Main category of the hero
/// </Summary>
/// <Typeparam name = "T"> generic parameter, accept item type </typeparam>
Class hero <t> where T: iweapon, new ()
{
Private int _ attackvalue;
/// <Summary>
/// Attack Value
/// </Summary>
Public int attackvalue
{
Get {return _ attackvalue ;}
Set {_ attackvalue = value ;}
}
Public hero ()
{
// Initialize the lifecycle
This. _ attackvalue = 100;
}
/// <Summary>
/// Configure weapons
/// </Summary>
/// <Param name = "goods"> weapon type </param>
Public void addarm (T weapon)
{
This. _ attackvalue + = (iweapon) weapon). weaponvalue;
}
}
}
In this example, the Hero class is defined as: class hero <t> where T: weapon, new (), indicating that the Hero class defines a generic parameter t, the addarm Method Used for weapon equipment. However, two constraints are defined for this parameter. One is the implementation of the weapon interface, and the other is the constructor with or without parameters. Of course, you can also define more weapons for heroes, provided that the weapon interface must be implemented. The running result of this program is as follows:
In addition to the constraints mentioned above, there are also the following types of generic constraints:
T: Structure |
The type parameter must be a value type. You can specify any value types other than nullable. |
T: Class |
The type parameter must be a reference type, including any class, interface, delegate, or array type. |
T: New () |
The type parameter must have a public constructor without parameters. When used together with other constraints,New ()The constraint must be specified at the end. |
T: <Base Class Name> |
The type parameter must be a specified base class or derived from the specified base class. |
T: <Interface Name> |
The type parameter must be a specified interface or implement the specified interface. Multiple interface constraints can be specified. The constraint interface can also be generic. |
T: u |
The type parameter provided for T must be provided for U or derived from U. This is called the bare type constraint. |
These generic constraints can be used in combination, but they also have certain restrictions. For example, T: class cannot be used with T: struct. This is easy to understand because one is a value type, one is the reference type. There are also some other restrictions. We will not list them one by one here. I will try to find them in practice. The main purpose of this article is to understand the principles.
Summary. In fact, generics are not difficult to understand. The key is to figure out its design purpose, which is to solve the unsafe factors and performance loss caused by type conversion, the principle is to replace the type definition with a placeholder before secondary compilation, and then replace the placeholder with the specified type one by JIT, and finally compile. So I call it pseudo-generic!
I suddenly came up with another idea. In vs2008, I had an implicit type var. Can this be regarded as a real generic type? Please give me some comments!