C # generics detailed

Source: Internet
Author: User

What is a generic type

When we write a program, we often encounter the function of two modules is very similar, just one is the processing of int data, the other is processing string data, or other custom data types, but we have no way, can only write multiple methods to process each data type, because the method parameter type is different. Is there a way to pass in a common data type in a method so that you can't merge the code? The emergence of generics is a special solution to this problem. After reading this article, you will have a deeper understanding of generics.

Why use generics to understand this problem, we look at the following code, the code omitted some content, but the function is to implement a stack, the stack can only handle the INT data type:

 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? Many people would have thought of copying the above code, and changing int to string. Of course, this does not have any problem in itself, but a good program is not to do so, because he thought that if you need a long, node type of stack to do? Do you want to copy it again? A good programmer would think of using a generic data type object to implement the stack:

 Public class Stack    {        privateobject[] m_item;          Public Object Pop () {...}          Public void Push (object  Item) {...}          Public Stack (int  i)        {            thisnew[i];        }         }

This stack is well written, he is very flexible, can receive any data type, can be said once and for all. But in a comprehensive manner, it is not without blemish, and it is mainly manifested in:

When a stack processes a value type, a boxing, folding operation occurs, which allocates and reclaims a large number of variables on the managed heap, and if the amount of data is large, the performance penalty is severe. When working with reference types, there is no boxing and folding operations, but the use of a data type cast operation increases the burden on the processor. There is a more serious problem with casts on data types (assuming that the stack is an instance of the stack): Node1 x = new Node1 ();

  Stack. Push (x);          = (Node2) stack. Pop ();

The above code is completely no problem at compile time, but because of the push of a Node1 type of data, but in the pop is required to convert to the Node2 type, this will occur when the program runtime type conversion exception, but escaped the compiler check.

For the problem of the object type stack, we introduced 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, and the runtime (runtime) is automatically compiled into local code, and the efficiency and code quality are greatly improved, and the data type is guaranteed to be secure.

Using generics the following is a generic to rewrite the stack above, with a common 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)        {            thisnew  t[i];        }}

Class is not changed, just the introduction of the generic data type T can be applied to any data type and is type-safe. The calling method for this class:

Instantiation can only hold classes of type int

stack<intnew stack<int> (+);      A.push (ten);      A.push ("8888"// This line compilation does not pass because Class A only receives data      of type int int x = A.pop ();

Instantiation can only hold classes of type string

stack<stringnew stack<string> (b.push    ); // This line of compilation does not pass because Class B only receives data of type string       B.push ("8888"); string y = B.pop ();

There are distinct differences between this class and the class implemented by object:

1. He is type-safe. If you instantiate a stack of type int, you cannot handle data of type string, and so do other data types.

2. No packing and folding boxes are required. This class, when instantiated, 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.

The theory of generic class instantiation C # Generic classes at compile time, Mr. Cheng is the intermediate code IL, and the generic type T is just a placeholder. When instantiating a class, replacing T with a user-specified data type and generating native code from the immediate compiler (JIT), the native code already uses the actual data type, equivalent to a class written in the actual type, so the local code of the different enclosing classes is not the same. According to this principle, we can think that:

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

Examples:stack<int> and stack<string> are two classes that do not have any relationship at all, and you can see him as Class A and Class B, an explanation that is useful for understanding the static members of a generic class.

Constraints on data types in a generic class when writing a generic class, programmers always intentionally or unintentionally assume that the generic data type T is not generally suitable for all types, but how do you limit the type of data that the caller passes 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. This is the new keyword for c#2.0:

 Public class where T:stack, IComparable         where V:stack    {...}

The constraints of the above generic classes indicate that T must inherit from stack and IComparable, v must be a stack or inherit from a stack, otherwise it will fail to compile with the compiler's type check.

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

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

 Public class where New ()        where v:icomparable

Note that the new constraint can only be parameterless, so it is also required that the corresponding class stack must have an parameterless constructor, or the compilation will fail.

There are two main classes of data types in C #: Reference types and value types. Reference types such as all classes, value types are generally the most basic type of language, such as int, long, struct, etc., in the constraints of generics, we can also restrict a wide range of type T must be a reference type or must be a value type, the corresponding keyword is class and struct:

 Public class where class        where struct

Generic method generics not only function on the class, but also can be used alone in the method of the class, he can automatically adapt to various parameters according to the type of the method parameter, such method is called the generic method. Look at the following class:

 Public class Stack2    {        publicvoidparams  t[] p)        {            foreach in p)            {                s.push (t);         }}}

The original class stack can only push one data at a time, and this class Stack2 extends the functionality of the stack (which can also be written directly in the stack), and he can press 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 =Newstack<int> ( -); Stack2 X2=NewStack2 (); X2. Push (x,1,2,3,4,6); strings ="";  for(inti =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 among the same enclosing classes, and are not shared among different enclosing classes.

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

stack<intnew stack<int>(); Stack<intnew stack<int>(); Stack<longnew stack<long> ();

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

Static constructors in generics rules for static constructors: there can only be one, and cannot have parameters, he can only be. NET runtime is called automatically, and cannot be called manually.

The principle of static constructors in generics is the same as for non-generic classes, simply by understanding the different enclosing classes in generics as different classes. The following two scenarios can trigger a static constructor:

1. The specific enclosing class is instantiated for the first time.

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

Overloading of method overloading methods in generic classes is heavily applied in the. Net framework, and he requires that overloads have different signatures. In a generic class, because the generic type T is not deterministic when the class is written, there are some considerations when overloading, which we illustrate with the following examples:

 Public classNode<t, v>    {         PublicT Add (t A, V B)//First Add        {            returnA; }         PublicT Add (V a, T b)//a second add        {            returnb; }         Public intAddintAintb//a third add        {            returnA +b; }}

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

node<intintnew node<intint>();     object x = Node.add (2each);

This instantiation of node causes three add to have the same signature, but it can be invoked successfully because he first matches the third Add. However, if the third add is removed, the above calling code will not compile, prompting the method to produce confusion because the runtime cannot select between the first add and the second Add.

node<stringintnew node<stringint>();         object x = Node.add (2"One");

These two lines of calling code compile correctly, because the incoming string and int make the three add have different signatures and, of course, can find the only matching Add method.

As the above example shows, the generics of C # are when the method of an instance is called to check if the overload is confusing, rather than checking at compile time for the generic class itself. It also draws an important principle:

Generic methods are overwritten when they have the same signature as the generic method.

The main problem with method override method overrides for generic classes is the recognition rules for method signatures, which, like the method overloads, refer to the method overloads of the generic class.

Scope of use of generics this article is mainly about generics in the class, in fact, generics can also be used in class methods, interfaces, structures (structs), delegates and so on above, the use of the same method is similar, no longer tell.

Summary C # Generics are an invaluable asset in the development tool library. They can improve performance, type safety and quality, reduce repetitive programming tasks, and simplify the overall programming model, all done through elegant, readable syntax. Although the C # generics are rooted in C + + templates, C # raises generics to a new level by providing compile-time security and support. C # leverages two-phase compilation, metadata, and innovative concepts such as constraints and general methods. There is no doubt that future versions of C # will continue to evolve generics in order to add new functionality and extend generics to other. NET Framework areas such as data access or localization

C # generics detailed

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.