What is genericsWhen 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 above code 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 (object) {...} public Stack (int i) { This.m_item = New[i]; } } This stack is well written, He is very flexible and can receive any data type, which can be said to be once and for all. But comprehensively speaking, is also not without flaw, the main manifestation is: When stack handle value type, will appear boxing, folding box operation, this will allocate and reclaim a large number of variables on the managed heap, if the amount of data is large, the performance loss is very serious. 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 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 object type stack problem, 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) & nbsp; { 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 invocation method of this class://instantiation can only save class Stack <int> a = new stack<int> (100) of type int; 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 class Stack <string> b = new stack<string> (100); B.push (a) //this line of compilation does not pass because class B receives only string-type data B.push ("8888"); string y = B.pop (); This class and object-implemented classes have distinct differences: 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 packing and folding boxes. 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 instantiationAt 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 assume that the different enclosing classes of the generic class are 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, V must be stack or inherit from stack, or it will fail to be checked by the compiler's type. Compilation failed. 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 need to note that the new constraint can only be parameterless, so it also requires that 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 generic constraints, we can also limit the type T must be a reference type or must be a value type, respectively, the corresponding keyword is class and strucT:public class Node<t, v> where T:class where v:struct
Generic Methods 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++) {&NBSP;&NBSP;&NBSP;&NBSP;&NBsp; s + = X.pop (). ToString (); } //At this point, s value is 64321
static member variables in genericsIn 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 typeStatic 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. The specified enclosing 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> { & nbsp; public T Add (t A, V B) //First Add & nbsp; { return A; } public T Add (V a, T b) & nbsp; //Second add { return B; } public int add (int a, int b) & nbsp; //Third add { return a + B; &nBsp The class above is obvious, 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. Consider 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 string and int passed in make three add have different signatures, and of course find the only matching Add method. As the above example shows, the generics of C # are to check whether the overload is confusing when the instance's method is invoked, rather than to check the generic class itself at compile time. It is also important to conclude that generic methods are overwritten when the generic method has the same signature as the general method.
method overrides for generic classesThe main problem with method overrides (override) is the recognition rule for method signatures, and at this point, as with method overloads, refer to the method overloads of generic classes.
scope of use of genericsThis article is mainly about generics in the class, in fact, generics can also be used in class methods, interfaces, structures (struct), delegates, etc. used above, the use of the same method, no longer described.
SummaryC # generics are a priceless asset in the development tool library. They can improve performance, type safety, and quality, reduce repetitive programming tasks, and simplify the overall programming model, which is done with elegant, readable syntax. Although C # generics are rooted in C + + templates, C # raises generics to a new level by providing compile-time security and support. C # leverages two-stage compilation, metadata, and innovative concepts such as constraints and general methods. There is no doubt that future versions of C # will continue to develop generics to add new functionality and extend generics to other. NET Framework areas such as data access or localization.
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.