C # Understanding Generics

Source: Internet
Author: User

Source: Http://www.tracefact.net/CSharp-Programming/Generics-In-CSharp.aspx Glossary

Generics: Generics
Type-safe: Type safety
Collection: Collection
Compiler: Compiler
Run Time: Program runtime
Object: Objects
. NET Library:. NET class Library
Value type: value types
Box: Boxed
Unbox: Unpacking
Implicity: Implicit
Explicity: an explicit
Linked list: linear linked list
Node: node
Indexer: Indexer

Brief introduction

One of the most anticipated (and perhaps most daunting) features of Visual C # 2.0 is support for generics. This article will tell you what kind of problems generics are used to solve, how to use them to improve your code quality, and why you don't have to fear generics.

What is a generic type?

Many people feel that generics are hard to understand. I believe this is because they are often taught a lot of theories and examples before they understand what generics are used to solve. The result is that you have a solution, but there is no problem needing to use this solution.

This article will try to change this learning process, and we'll start with a simple question: what is generics for? The answer is: without generics, it would be difficult to create a collection of type-safe.

C # is a type-safe language, and type safety allows the compiler (trustworthy) to capture potential errors rather than discovering them when the program is running (untrustworthy, which often happens after you sell the product!). )。 Therefore, in C #, all variables have a defined type, and when you assign an object to that variable, the compiler checks to see if the assignment is correct, and if there is a problem, the error message is given.

In the. Net 1.1 Release (2003), this type of security is invalidated when you use a collection. All the classes that are provided by the. NET class library are used to store the base type (object), and everything in. NET is inherited by the Object base class, so all types can be placed in a collection. Thus, the equivalent of no type detection at all.

Even worse, every time you take an object from the collection, you must cast it to the correct type, which will affect performance and produce lengthy code (an exception will be thrown if you forget to convert). Further, if you add a value type (for example, an integer variable) to the collection, the integer variable is implicitly boxed (once again degrades performance), and when you take it out of the collection, it is explicitly unboxing (again performance reduction and type conversion).

For more information on packing and unpacking, please visit Trap 4 and beware of implicit boxing and unpacking.

Create a simple linear list

To feel these problems vividly, we will create a linear list that is as simple as possible. For those reading this article, those who have never created a linear list. You can think of a linear list as a box with a chain tied together (called a node), each containing some data and a reference to the next box on the chain (except, of course, the box's reference to the next box is set to NULL in addition to the last box).

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

1, node class, contains data and a reference to the next node.

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

3, test program, for testing LinkedList class.

To see how the linked table works, we add two types of objects to the linked list: integer and employee types. You can think of the employee type as a class that contains all the 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 contains only a string type that represents the employee's name, a constructor that sets the employee's name, and a ToString () method that returns the employee name.

The linked table itself is made up of a lot of node, which, as mentioned above, must contain data (integer and Employee) and a reference to the next node in the 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 The constructor sets the private data member to the passed-in object and sets the next field to null.

This class also includes a method, Append, which takes a node-type parameter, and we will add the passed-in node to the last position in the list. The process is this: first detect the current node's next field to see if it is null. If so, now node is the last node, we point the current node's next property to the new node passed in, so we insert the new node into the tail of the 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 next field type is also node, we call the next field's Append method (Note: recursive invocation) and pass the node parameter again, so that it continues 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 overridden to output the value in data and invokes the ToString () method of the next node (called again recursively).

public override string ToString () {
string output = data. ToString ();

if (next! = null) {
Output + = "," + next. ToString ();
}

return output;
}

Thus, when you invoke the ToString () method of the first node, the value of node on all linked lists is printed out.

The LinkedList class itself contains only a reference to a node called Headnode, which is the first node in the list, initialized to null.

public class linkedlist{
Node headnode = null;
}

The LinkedList class does not require constructors (default constructors created with the compiler), but we need to create a public method, ADD (), which stores data in a linear list. This method first checks that Headnode is not NULL, and if so, it will use data to create the node, and the node as Headnode, if it is not NULL, it will create a new node containing data, and invoke Headnode's Append method, 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 collection, we create an indexer for the linear list.

public object this[ int index]{
    get{
        int ctr = 0;
       node node = Headnode;
       while  (node!= null  && Ctr <= index) {
& nbsp;          if  (ctr = = index) {
               return node. Data;
          }else{
               node = node. Next;
          }
           ctr++;
       }
    return null;
   }
}

Finally, the ToString () method is overwritten again to invoke the ToString () method of Headnode.

public override string ToString () {
if (This.headnode! = null) {
return this.headNode.ToString ();
}else{
return string. Empty;
}
}

Testing a linear linked list

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

public void Run () {
LinkedList ll = new LinkedList ();
for (int i = 0; i <; 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 result of the output confirms that both the integer value and the employee type are stored in the same collection.

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.

While it may seem convenient, the downside is that you lose all type-safe features. Because the linear list requires an object type, each integer value added to the collection is implicitly boxed, as shown in the IL code:

Il_000c:box [Mscorlib]system.int32
Il_0011:callvirt instance void Objectlinkedlist.linkedlist::add (object)

Similarly, if you remove an item from your list, these integers must be explicitly disassembled (cast to Integer), and the employee type must be cast 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 type-safe collection. An employee linear list will not accept the Object type; it accepts only instances of the employee class (or inherited from an instance of the Employee Class). This would be type-safe and would no longer require a type conversion. An integer linear list that will no longer require boxing and unboxing (because it can only accept integer values).

As an example, you will create a employeenode that knows the type of data it is employee.

public class Employeenode {
Employee EmployeeData;
Employeenode Employeenext;
}

The Append method now accepts a parameter of type Employeenode. 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 accepts an employee:

public void Add (Employee data) {
if (Headnode = = null) {
Headnode = new Employeenode (data);}
else{
Headnode.append (new Employeenode (data));
}
}

Similarly, indexers must be modified to accept employeenode types, and so on. This does solve the problem of boxing, unpacking, and adding type-safe features. You can now add the employee (but not the integer) to your new linear list, and when you remove the employee from it, the type conversion is no longer necessary.

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);

Well, when there's an integer trying to implicitly convert to the employee type, the code can't even pass the compiler!

But its bad thing is: every time you need to create a type-safe list, you need to do a lot of copy/paste. Not good enough, 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 of link list should be accepted, so you will have to give the class user the job of adding type safety as a mechanism---your users.

Using generics to achieve code reuse

The solution, as you might imagine, is to use generics. With generics, you re-get the code for the list of links to be generic (only implemented once for all types), and when you initialize the linked lists, you tell the chain to accept the type. This implementation is very simple, let's go back to the node class:

public class node{
Object data;
...

Note that the type of data is object, (in Employeenode, it is an employee). We'll turn it into a generic (usually, represented by an uppercase T). We also define the node class, which means that it can be generalized to accept a T-type.

public class Node <t>{
T data;
...

Read as: node of type T. T represents the type that node accepts when node is initialized. T can be an object, an integer, or an employee. This can be determined when node is initialized.

Note: Using T as an identity is only a conventional one, and you can use other combinations of letters instead, such as:

public class Node <unknowntype>{
Unknowntype data;
...

By using T as an unknown type, the next field (a reference to the next node) must be declared as node of type T (meaning to accept a generic node of type T).

Node<t> Next;

The constructor accepts a simple parameter of type T:

Public Node (T data)
{
This.data = data;
This.next = null;
}

The rest of the Node class is simple, and all you need to do with object is now you need to use T. The LinkedList class now takes a node of type T, rather than a simple node as the head node.

public class linkedlist<t>{
Node<t> headnode = null;

Once again, the conversion is straightforward. Anywhere you need to use object, now change to T, any place where you need to use Node, now do node<t>. The following code initializes two of the linked tables. One is an integral type.

linkedlist<int> ll = new linkedlist<int> ();

The other is the employee type:

Linkedlist<employee> employees = new linkedlist<employee> ();

The remainder of the code is no different from the first version, except that there is no boxing, unpacking, and it is not possible to save the wrong type to the collection.

linkedlist<int> ll = new linkedlist<int> ();
for (int i = 0; i <; i + +)
{
ll. ADD (i);
}

Console.WriteLine (ll);
Console.WriteLine ("Done.");

Linkedlist<employee> employees = new linkedlist<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 type-safe collections without having to copy/paste lengthy code. Also, because generics are extended to special types at run time. The Just in time compiler can share code between different instances, and finally, it significantly reduces the code you need to write.

C # Understanding Generics

Related Article

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.