C # 's generics detailed

Source: Internet
Author: User

What is generics


When we write a program, we often encounter two modules that are very similar to each other, just one for the int data, the other for string data, or other custom data types, but there is no way to write each data type separately, because the method has different parameter types. Is there a way to pass in a common data type in a method so that the code can be merged? The appearance of generics is a special solution to this problem. After reading this article, you will have a deeper understanding of generics.

Why to use generics
To understand this, let's look at the following code, which omits some of the content, but the function is to implement a stack that can only handle int data types:

public class Stack

{

Private int[] M_item;

public int Pop () {...}

public void Push (int item) {...}

Public Stack (int i)

{

This.m_item = new Int[i];

}

}

The code above works fine, but what happens when we need a stack to hold the string type. A lot of people would think of copying the above code, and changing the int to a string would be fine. Of course, there is no problem with doing so, but a good program is not going to do that, because he thought about what to do with long, node-type stacks later. Do you want to copy it again? A good programmer would think of using a generic data type object to implement this stack:

public class Stack

{

Private object[] M_item;

public Object Pop () {...}

public void Push (item) {...}

Public Stack (int i)

{

This.m_item = New[i];

}

}

This stack is well written, he is very flexible, can receive any data type, can be said to be once and for all. But comprehensively speaking, is also not without flaw, mainly manifests in:

When stack handles value types, boxing and folding operations occur, which allocates and reclaims a large number of variables on the managed heap, and the performance loss is severe if the data is large.
When handling reference types, there is no boxing and folding operations, but the data type cast operation is used to increase the burden on the processor.
There are more serious problems with casts on data types (assuming that stack is an instance of stack):
Node1 x = new Node1 ();

Stack. Push (x);

Node2 y = (Node2) stack. Pop ();

The above code is completely fine at compile time, but because of the push of a Node1 type of data, but the need to convert to the Node2 type when the pop, this will occur when the program runs a type conversion exception, but fled the compiler's check.

For the problem with the object type stack, we introduce generics, and he can solve these problems gracefully. Generics use a passing data type T instead of object, specifying the type of T when the class is instantiated, the runtime (Runtime) is automatically compiled into local code, and the efficiency and quality of the code are greatly improved and the data type is secured.

Using generics
The following is the use of generics to rewrite the above stack, using a generic data type T as a placeholder, waiting to be instantiated with an actual type instead. Let's take a look at the power of generics:

public class Stack<t>

{

Private t[] M_item;

Public T Pop () {...}

public void Push (T item) {...}

Public Stack (int i)

{

This.m_item = new T[i];

}

}

The class is written in the same style, except that the universal data type T can be applied to any data type and is of type safety. The calling method for this class:

Instantiation can only save classes of type int

Stack<int> a = new stack<int> (100);

A.push (10);

A.push ("8888"); This line of compilation does not pass because Class A only receives data of type int

int x = A.pop ();

Instantiation can only save string-type classes

Stack<string> B = new stack<string> (100);

B.push (10); This line of compilation does not pass because class B receives only string-type data

B.push ("8888");

String y = B.pop ();

This class has a distinct difference from the class that the object implements:

1. He is of type safety. When you instantiate a stack of type int, you cannot handle data of type string, as are other data types.

2. No boxing and folding boxes are required. When instantiated, this class generates local code according to the data type passed in, and the local code data type is determined, so no boxing and folding boxes are required.

3. No type conversion is required.

Theory of generic class instantiation
At compile time, the C # generic class is the intermediate code IL, and the universal type T is just a placeholder. When instantiating a class, the local code for a different enclosing class is not the same, based on the user-specified data type instead of T and the local code generated by the Just-in-time compiler (JIT), which already uses the actual data type in the local code, equivalent to the class written with the actual type. According to this principle, we can say that:

The different enclosing classes of a generic class are of different data types.

For example,:stack<int> and stack<string> are two completely without any relationship, and you can think of him as Class A and Class B, which is helpful for the understanding of static members of generic classes.

Constraints on data types in generic classes
When programmers write generic classes, they always have an intentional or unintentional assumption of universal data type T, which means that this t generally doesn't fit all types, but how to limit the type of data that the caller is passing in. This requires constraining the incoming data type by specifying the ancestor of T, which is the inherited interface or class. Because of the single root inheritance of C #, a constraint can have multiple interfaces, but only one class at most, and the class must precede the interface. The New keyword for c#2.0 is used:

public class node<t, v> where T:stack, IComparable

where V:stack

{...}

The constraints of the generic class above indicate that T must be inherited from stack and icomparable, that v must be stack or inherited from stack, or that the compilation fails without the type checking of the compiler.

Generic type T is not specified, but because all classes in C # are inherited from object, he can only invoke the method of the object class in the writing of class node, which makes it difficult to write a program. For example, your class design only needs to support two data types int and string, and you need to compare the size of variables of type T in the class, but these cannot be implemented because object is not a method of comparing size. To solve this problem, we only need to icomparable the T, then we can execute the CompareTo method on the instance of T in class node. This problem can be extended to other user-defined data types.

What if you need to instantiate T again in class node? Because class T does not know exactly what constructors are in class node. To solve this problem, you need to use the new constraint:

public class node<t, v> where T:stack, new ()

where v:icomparable

It is important to note that the new constraint can only be parameterless, so the corresponding class stack must have an parameterless constructor, otherwise the compilation fails.

There are two broad classes of data types in C #: Reference types and value types. Reference types, such as all classes, value types are generally the most basic types of languages, such as int, long, struct, and so on, in the context of a generic constraint, we can also limit the type T to be a reference type or must be a value type, and the corresponding keyword is class and struct:

public class node<t, v> where T:class

where v:struct

Generic method
Generics can be used not only on classes, but also on class methods, which automatically adapt to various parameters based on the type of method parameters, such as generic methods. Look at the following class:

public class Stack2

{

public void push<t> (stack<t> s, params t[] p)

{

foreach (T t in P)

{

S.push (t);

}

}

}

The original class stack can only push one data at a time, and the class Stack2 expands the stack's functionality (and, of course, it can be written directly in the stack), and he can push multiple data into the stack at once. Where push is a generic method, the invocation example of this method is as follows:

stack<int> x = new stack<int> (100);

Stack2 x2 = new Stack2 ();

X2. Push (x, 1, 2, 3, 4, 6);

string s = "";

for (int i = 0; i < 5; i++)

{

S + + X.pop (). ToString ();

//Thus, the value of S is 64321

Static member variables in generics
In c#1.x, we know that static member variables of a class are shared among different class instances, and that he is accessed through the class name. Due to the introduction of generics in c#2.0, there are some changes in the mechanism of static member variables: Static member variables are shared between the same enclosing classes, and different enclosing classes are not shared.

This is also very easy to understand because different enclosing classes have the same class name, but because they are passed into different data types, they are completely different classes, such as:

Stack<int> a = new stack<int> ();

Stack<int> B = new stack<int> ();

stack<long> C = new stack<long> ();

class instances A and B are the same type, they share static member variables, but class instance C is completely different from A and B, so you cannot share static member variables with a and B.

A static constructor in a generic type
Static constructor rules: Only one, and cannot have parameters, he can only be. NET runtime is invoked automatically, not manually.

The principle of a static constructor in a generic type is the same as a non-generic class, which simply understands the different enclosing classes in the generics as distinct classes. The following two scenarios can trigger a static constructor:

1. A particular closed class is instantiated for the first time.

2. Any static member variable in a particular enclosing class is invoked.

Method overloads in a generic class
The overload of a method is applied heavily in the. Net framework, and he requires that the overload have a different signature. In a generic class, because common type T is not certain when the class is written, there are some considerations in overloading, which we illustrate with the following example:

public class Node<t, v>

{

Public T Add (t A, V B)//First add

{

return A;

}

Public T Add (V a, T b)//second add

{

return b;

}

public int Add (int a, int b)//Third add

{

return a + B;

}

}

The above class is clear, if both T and V are passed in int, the three Add method will have the same signature, but the class can still be compiled, whether it will cause call confusion to be judged when this class instantiates and invokes the Add method. Take a look at the following calling code:

Node<int, int> node = new Node<int, int> ();

Object x = Node.add (2, 11);

The instantiation of this node causes three add to have the same signature, but it can invoke success because he first matches the third Add. If you delete the third add, however, the calling code above cannot be compiled to prompt for confusion because the runtime cannot choose between the first add and the second Add.

Node<string, int> node = new node<string, int> ();

Object x = Node.add (2, "11");

The two lines of the calling code are compiled correctly, because the incoming string and int allow three add to have a different signature, and of course, the only matching Add method can be found.

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.