C # Understanding Generic Learning Notes

Source: Internet
Author: User
Tags constructor

Terminology List

Generics: Generics

Type-safe: Type safety

Collection: Collection

Compiler: Compiler

Run Time: Program runtime

Object: Objects

. NET Library:. NET class Library

Value type: Values types

Box: Boxing

Unbox: Unboxing

Implicity: Implicit

Explicity: Explicit

Linked list: Linear list

Node: node

Indexer: Indexers

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 generics are used to solve, and how to use them to improve the quality of your code, and why you don't have to be afraid of generics.

What is a generic type?

A lot of people think generics are hard to understand. I believe this is because they are often taught a lot of theories and paradigms before they understand what generics are used to solve. The result is that you have a solution, but there is no need to use the solution.

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

C # is a type-safe language, and type safety allows the compiler (reliably) to catch potential bugs rather than discovering them when the program is running (untrustworthy, often 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 to see if the assignment is correct, and if there is a problem, an error message is given.

In the. Net 1.1 version (2003), this type of security is invalidated when you use a collection. All 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. As a result, the equivalent of no type detection at all.

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

For more information on boxing, unpacking, visit trap 4, beware of implicit boxing and unboxing.

Create a simple linear list

To get a vivid feel for these questions, we'll create a linear list as simple as possible. For those who read 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 box contains some data and references to the next box on the chain (of course, except for the last box, the box has a reference to the next box that is set to null).

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

1, node class, contains data and references to the next node.

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

3, test procedures, for testing LinkedList class.

To see how the linked tables work, we add two types of objects to the list: integer and employee types. You can imagine the employee type as a class that contains all the information about an employee in a 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's name.

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

This class also includes a method, Append, that accepts a node type parameter, and we will add the node passed in to the last position in the list. This is the process of first detecting the next field of the current node to see if it is null. If so, the current node is the last node, and we point the current node's next attribute to the new node that is passed in, so we insert the new node at the end of the list.

If the next field of the current node is not NULL, the current node is not the last node in the list. Because the type of the next field is also node, we call the Append method of the next field (note: recursive invocation), pass the node parameter again, and 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 to output the value in data and calls 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;

}

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

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

public class linkedlist{

Node headnode = null;

}

The LinkedList class does not require constructors (using the default constructor created by the compiler), but we need to create a public method, ADD (), which stores data in a linear list. This method first checks that the headnode is not NULL, and if so, it uses data to create the node and use the node as Headnode, if not NULL, it creates a new node that contains data and invokes the Headnode 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 little set, 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) {

if (ctr = = index) {

Return node. Data;

}else{

node = node. Next;

}

ctr++;

}

return null;

}

}

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

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 list to test:

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 type object, 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 this may seem convenient, the downside is that you lose all types of security features. Because a 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 items from your list, the integral types 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 Object types; it only accepts instances of the employee class (or inherits from instances of the employee Class). This will be type-safe and no longer requires type conversions. A linear list of integral types that will no longer require boxing and unboxing operations (since it can only accept integer values).

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

public class Employeenode {

Employee EmployeeData;

Employeenode Employeenext;

}

The Append method now accepts an argument of a employeenode type. You also need to create a new employeelinkedlist that 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, unboxing, and adding type-safe features. You can now add an employee (but not an integer) to your new linear list, and when you remove the employee from it, you no longer need type conversions.

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 so good, when an integral type attempts to implicitly convert to the employee type, the code cannot even pass the compiler!

But the bad part is: Every time you need to create a type-safe list, you need to do a lot of copy/paste. It's not good enough to have code reuse at all. At the same time, if you are the author of this class, you may not even want to know in advance what type of link list should be accepted, so you have to give the class user the job of adding type safety to---your users.

Use generics to achieve code reuse

The solution, as you would guess, is to use generics. By generics, you regain the generic code for the list of links (only once for all types), and when you initialize the linked lists, you tell the types that the linked tables can accept. 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 employee). We'll turn it into a generic (typically, represented by an uppercase T). We also define the node class to indicate 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 object, or it may be an integral type or an employee. This can be determined when node is initialized.

Note: Using T as an identity is a common use, and you can substitute other combinations of letters, such as:

public class Node <unknowntype>{

Unknowntype data;

...

By using T as an unknown type, the next field (the 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, all the places you need to use object, you need to use T now. The LinkedList class now accepts a node of type T, rather than a simple node as the header node.

public class linkedlist<t>{

Node<t> headnode = null;

Again, the conversion is straightforward. Anywhere you need to use object, now do T, wherever you need to use Node, now do node<t>. The following code initializes two 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 rest of the code is no different from the first version, except that there is no boxing, unboxing, 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 duplicating/pasting 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.

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.