[Translation] C # understanding generics

Source: Internet
Author: User
PDF browse: http://www.tracefact.net/document/generics-in-csharp.pdf
Source code download: http://www.tracefact.net/SourceCode/Generics-In-CSharp.rar
Output: http://www.ondotnet.com/pub/a/dotnet/2004/05/17/liberty.htmlC# understanding generic Glossary

Generics: Generic
Type-safe: type security
Collection: Set
Compiler: Compiler
Run Time:ProgramRuntime
Object: Object
. Net Library:. Net class library
Value Type: Value Type
Box: Packing
Unbox: Unbox
Implicity: Implicit
Explicity: explicit
Linked List: Linear Linked List
Node: Node
Indexer: Indexer

Introduction

One of the most anticipated (perhaps the most daunting) features of Visual C #2.0 is its support for generics. This articleArticleIt will tell you what problems are solved by generics and how to use them to improve yourCodeQuality, and you don't have to fear generics.

What is generic?

Many people think that generics are hard to understand. I believe this is because they have been instilled in a large number of theories and examples before learning about the problems that generics are used to solve. The result is that you have a solution, but you do not need to use it.

This article will try to change this learning process. We will start with a simple question: what is generic used? The answer is: if there is no generic type, it will be difficult to create a type-safe set.

C # Is a type-safe language. type security allows the compiler (trusted) to capture potential errors, rather than discovering (untrustworthy, it often happens after you sell the product !). Therefore, in C #, all variables have a defined type. When you assign an object to that variable, the compiler checks whether the value is correct, if any problem occurs, an error message is displayed.

In. NET 1.1 (2003), this type of security becomes invalid when you are using a set. By.. Net Class Library provides all classes about the set to store the basic type (object), and.. net, all types are inherited by the object base class, so all types can be put in a collection. Therefore, there is no type check at all.

Worse, every time you extract an object from the set, you must forcibly convert it to the correct type. This conversion will affect the performance, and produce lengthy code (if you forget to convert it, an exception will be thrown ). Furthermore, if you add a value type (for example, an integer variable) to the set, this integer variable is implicitly boxed (reducing performance again ), when you extract it from the set, it will be displayed again (performance reduction and type conversion again ).

For more information about packing and unpacking, visit trap 4 to be cautious about Implicit packing and unpacking.

Create a simple linear linked list

We will create a Linear Linked List as simple as possible to give a vivid look at these problems. For those who have never created a Linear Linked List. You can think of a Linear Linked List as a box with a chain tied together (called a node). Each box contains some data and references to the next box on the chain (of course, except the last box, the reference to the next box is set to null ).

To create a simple linear linked list, we need the following three classes:

1. node class, including data and references of the next node.

2. The linked list class contains the first node in the linked list and any additional information about the linked list.

3. The test program is used to test the shortlist class.

To view how a linked table works, we add two types of objects to the linked list: integer and employee. You can think of the employee type as a class that contains all information about an employee in the company. For demonstration purposes, the employee class is very simple.

Public class employee {
Private string name;
Public Employee (string name ){
This. Name = Name;
}

Public override string tostring (){
Return this. Name;
}
}

This class only contains a string type that represents the employee name, a constructor that sets the employee name, and a tostring () method that returns the employee name.

The linked table itself is composed of many nodes. These notes, as mentioned above, must contain data (integer and employee) and references of the next node in the linked list.

Public class node {
Object Data;
Node next;

Public node (Object Data ){
This. Data = data;
This. Next = NULL;
}

Public object data {
Get {return this. Data ;}
Set {DATA = value ;}
}

Public node next {
Get {return this. Next ;}
Set {This. Next = value ;}
}
}

Note that the constructor sets the private data member as the passed object and sets the next field to null.

This class also includes a method, append, which accepts a parameter of the node type. We will add the passed node to the final position in the list. This process is as follows: First, check the next field of the current node to see if it is null. If yes, the current node is the last node. We direct the next attribute of the current node to the new node passed in. In this way, we insert the new node to the end of the linked list.

If the next field of the current node is not null, the current node is not the last node in the linked list. Because the type of the next field is also node, we call the append method of the next field (Note: recursive call) and pass the node parameter again, so that we can continue until the last node is found.

Public void append (node newnode ){
If (this. Next = NULL ){
This. Next = newnode;
} Else {
Next. append (newnode );
}
}

The tostring () method in the node class is also overwritten. It is used to output the value in data and call the tostring () method of the next node ).

Public override string tostring (){
String output = data. tostring ();

If (next! = NULL ){
Output + = "," + next. tostring ();
}

Return output;
}

In this way, when you call the tostring () method of the first node, the value of the node on all linked lists is printed.

The listlist class only contains references to one node. This node is called headnode and is the first node in the linked list. Its Initialization is null.

Public class category list {
Node headnode = NULL;
}

The struct list class does not need Constructor (the default constructor created by the compiler), but we need to create a public method, add (), which stores data in a linear linked list. This method first checks whether headnode is null. If yes, it will use data to create a node and use this node as the headnode. If not null, it will create a new node containing data, and call the append method of headnode, as shown in the following code:

Public void add (Object Data ){
If (headnode = NULL ){
Headnode = new node (data );
} Else {
Headnode. append (new node (data ));
}
}

To provide a sense of a set, we create an indexer for the linear linked list.

Public object this [int Index] {
Get {
Int CTR = 0;
Node node = headnode;
While (node! = NULL & CTR <= index ){
If (CTR = index ){
Return node. Data;
} Else {
Node = node. Next;
}
CTR ++;
}
Return NULL;
}
}

Finally, the tostring () method is overwritten again to call the tostring () method of headnode.

Public override string tostring (){
If (this. headnode! = NULL ){
Return this. headnode. tostring ();
} Else {
Return string. empty;
}
}

Test Linear Linked List

We can add some integer values to the linked list for testing:

Public void run (){
Counter list LL = new counter list ();
For (INT I = 0; I <10; I ++ ){
Ll. Add (I );
}

Console. writeline (LL );
Console. writeline ("done. Adding employees ...");
}

If you test this code, it will work as expected:

0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Done. Adding employees...

However, because this is a collection of object types, you can also add the employee type to the collection.

Ll. Add (new employee ("John "));
Ll. Add (new employee ("PAUL "));
Ll. Add (new employee ("George "));
Ll. Add (new employee ("ringo "));

Console. writeline (LL );
Console. writeline ("done .");

The output confirms that both the integer value and the employee type are stored in the same set.

0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Done. Adding employees...
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, John, Paul, George, Ringo
Done.

Although this seems convenient, the negative impact is that you have lost all types of security features. Because the Linear Linked List requires an object type, each integer value added to the set is implicitly boxed, as shown in the Il code:

Il _ 000c : Box [mscorlib] system. int32
Il_0011: callvirt instance void objectdetaillist. detaillist: add (object)

Similarly, if we say that when you extract a project from your list, these integers must be explicitly split (forcibly converted to an integer ), the employee type must be forcibly converted to the employee type.

Console. writeline ("The fourth integer is" + convert. toint32 (LL [3]);
Employee d = (employee) ll [11];
Console. writeline ("the second employee is" + D );

The solution to these problems is to create a set of security types. An employee Linear Linked List cannot accept the object type. It only accepts instances of the employee class (or instances inherited from the employee class ). In this way, the data type is safe and no type conversion is required. An integer linear linked list, which no longer needs to be packed or unpacked (because it can only accept integer values ).

As an example, you will create an employeenode, which knows that its data type is employee.

Public class employeenode {
Employee employeedata;
Employeenode employeenext;
}

The append method now accepts a parameter of the employeenode type. You also need to create a new employeelinkedlist, which accepts a new employeenode:

Public class employeelinkedlist {
Employeenode headnode = NULL;
}

The employeelinkedlist. Add () method no longer accepts an object but an employee:

Public void add (employee data ){
If (headnode = NULL ){
Headnode = new employeenode (data );}
Else {
Headnode. append (New employeenode (data ));
}
}

Similarly, the indexer must be modified to accept the employeenode type. This solves the problem of packing and unpacking, and adds the type security feature. Now you can add the employee (but not an integer) to your new Linear Linked List, and when you extract the employee, the type conversion is no longer needed.

Employeelinkedlist employees = new employeelinkedlist ();
Employees. Add (new employee ("Stephen King "));
Employees. Add (new employee ("James Joyce "));
Employees. Add (new employee ("William Faulkner "));
/* Employees. Add (5); // try to add an integer-won't compile */
Console. writeline (employees );
Employee E = employees [1];
Console. writeline ("the second employee is" + E );

This is good. When an integer tries to implicitly convert to the employee type, the Code cannot even pass the compiler!

But what's worse: every time you create a type-safe list, you need to copy/paste a lot. It is not good at all, and there is no code reuse at all. At the same time, if you are the author of this class, you cannot even know in advance what type the link list should accept, you have to hand over the work of adding the type security mechanism to the class user --- your user.

Use generics for code reuse

The solution, as you guess, is to use generics. Through generics, you get the code generic for the link list again (only one implementation for all types), and when you initialize the linked list, you tell the linked list the acceptable types. This implementation is very simple. Let's return to the node class:

Public class node {
Object Data;
...

Note that the data type is object (in employeenode, It is employee ). We will change it to a generic type (usually represented by a capital T ). We also define the node class, which means it can be generic to accept a T type.

Public class node <t> {
T data;
...

Read as: T-type node. T indicates the type accepted by the node when the node is initialized. T can be an object, an integer, or an employee. This can be determined only when the node is initialized.

Note: using t as the logo is just a convention. You can use another letter combination, for example:

Public class node <unknowntype> {
Unknowntype data;
...

By using t as the unknown type, the next field (reference of the next node) must be declared as a node of the T type (meaning that a node of the T type is accepted ).

Node <t> next;

The constructor accepts a simple T-type parameter:

Public node (t data)
{
This. Data = data;
This. Next = NULL;
}

The rest of the node class is very simple. All the places where you need to use objects, you need to use t now. The shortlist class now accepts a node of the T type, rather than a simple node as the header node.

Public class category list <t> {
Node <t> headnode = NULL;

Again, the conversion is straightforward. You need to use the object anywhere. Now you need to change it to T. If you need to use node, you need to change it to node <t>. The following code initializes two chain tables. One is an integer.

Counter list <int> LL = new counter list <int> ();

The other is of the employee type:

Topology list <employee> employees = new topology list <employee> ();

There is no difference between the remaining code and the first version, except that there is no packing or unpacking, and it is impossible to save the wrong type to the set.

Counter list <int> LL = new counter list <int> ();
For (INT I = 0; I <10; I ++)
{
Ll. Add (I );
}

Console. writeline (LL );
Console. writeline ("done .");

Topology list <employee> employees = new topology list <employee> ();
Employees. Add (new employee ("John "));
Employees. Add (new employee ("PAUL "));
Employees. Add (new employee ("George "));
Employees. Add (new employee ("ringo "));

Console. writeline (employees );
Console. writeline ("done .");
Console. writeline ("The fourth integer is" + ll [3]);
Employee d = employees [1];
Console. writeline ("the second employee is" + D );

Generics allow you to implement a type-safe set without copying/pasting lengthy code. In addition, the generic type is extended to a special type at runtime. The Just In Time compiler can share code between different instances. At last, it significantly reduces the code you need to write.

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.