NET generic programming is not far away from us. It is already available in. NET Framework 1.2 recently released by Microsoft with SQL Server Yukon Beta1. Although it still has many problems, I believe these problems will be solved with the official release of the new. NET Framework. Therefore, we should also prepare for. NET generic programming.
. NET is a single inheritance system. All types are derived from objects. I used to think that generics are not needed in a single inheritance system. Since everything can be passed as an Object, why should we use generics? It just adds complexity. Apart from seemingly more advanced, it seems that there is no other benefit. However, when two of the most famous single-host systems, Java and. NET, are all about to join generic programming, I have to review this question-why do we need generic programming?
In summary, generics have the following two advantages over non-generics:
1. More secure
In non-generic programming, although everything can be passed as an Object, type conversion is inevitable during the transfer process. Type conversion is not safe at runtime. Using generic programming can reduce unnecessary type conversion and improve security.
2. Higher efficiency
In non-generic programming, passing a simple type as an Object will cause packing and unpacking operations. Both of these processes are very open-ended. With generic programming, you do not need to pack or unpack the box.
. NET generics have good binary reusability. This is because. NET has built generics into CLR. Java generics in C ++ generics and evaluation rely on the features provided by their respective compilers. The Compiler expands the exact types when compiling generic code, this will inevitably lead to code expansion problems. The. NET generic code is compiled by JIT at runtime, so that CLR can reuse most of the real-time compilation code for different types.
The new C # will support generic programming features such as Generics and Iterator. Generics allow programmers to focus more on the design of different types of common algorithms, thus greatly improving the speed of development programs.
The general usage principle and implementation principle are to parameterize the operation type and transmit the information of the parameter type with generalized parameters. In this way, general algorithms such as List can simply write out a generic version. Different classes are generated during compiler compilation based on different generalized types without occupying the runtime time, this saves both development time and running time.
After talking about it for a long time, let's take a look at a generic example. You will have a real understanding of it.
Public class Stack
{
Object [] m_Items;
Public void Push (object item ){...}
Public object Pop (){...}
}
This is a stack code that does not use generics. Next let's take a look at how we use it.
Stack stack = new Stack ();
Stack. Push ("1 ");
String number = (string) stack. Pop ();
Stack stack = new Stack (); stack. Push (1 );
String number = (string) stack. Pop ();
During the use of this Code, the simple type will be boxed so that it can be pushed into the stack. In the pop-up process, a simple type of data needs to be removed to retrieve the value. This consumes a lot of computing time. The data type after unpacking is a forced conversion process, which may cause data type errors, leaving hidden risks.
Public class Stack
{
T [] m_Items;
Public void Push (T item ){...}
Public T Pop (){...}
}
Stack Stack = new Stack (); Stack. Push (1 );
Stack. Push (2 );
Int number = stack. Pop ();
This is the most common example of generics. When a generic class is generalized Stack... When this occurs, the compiler generates a class. The structure of this class is the same as that described in Stack. In particular, it uses int instead of T to generate a class. We assume that it is called intStack and all stack object operations, it is processed as an intStack type. In this way, if there are other general forms, the corresponding classes will be generated accordingly. If the generalized type is a reference type, the compiler converts it to the Stack class of the Object, that is, it does not make a generalization for the generic type of each reference type.
In this way, although the size of the generated program increases, the programming time and running time are greatly reduced, and the type security of the program is also improved. When you program a container's generic type, such as Stack, you will inevitably encounter operation problems on a single data in the container. The solution is the iterator to be introduced below.
The foreach statement in C # is used to iterate the elements in an enumerable set. To implement enumerable functions, a set must have a GetEnumerator method with no parameters and an enumerator. Generally, enumerators are difficult to implement. Therefore, it is important to simplify the tasks of enumerators.
Iterator is a statement block that generates an ordered sequence of (yields) values. The iterator distinguishes one or more yield statements from other statement blocks:
• The yield return Statement generates the next value of this iteration.
• The yield break statement indicates that this iteration is complete.
As long as the return value of a function member is an enumerator interfaces or enumerable interfaces, we can use the iterator:
• The so-called enumerators refer to System. Collections. IEnumerator and The constructed type.
• The enumerated interface refers to System. Collections. IEnumerable and The constructed type.
It is important to understand that an iterator is not a member, but a function member. A member implemented by the iterator can be overwritten or overwritten by one or more members that use or do not use the iterator.
The following Stack Class implements its GetEnumerator method using the iterator. The iterator enumerates the elements in the stack in the order from the top to the end.
Using System. Collections. Generic;
Public class Stack : IEnumerable
{
T [] items;
Int count;
Public void Push (T data ){...}
Public T Pop (){...}
Public IEnumerator GetEnumerator (){
For (int I = count-1; I> = 0; -- I ){
Yield return items [I];
}
}
}
The GetEnumerator method makes the Stack To be an enumerated type, which allows the Stack The instance uses the foreach statement. In the following example, values 0 to 9 are pushed into an integer stack, and each value is displayed in the order from the top to the end using the foreach loop.
Using System;
Class Test
{
Static void Main (){
Stack Stack = new Stack ();
For (int I = 0; I <10; I ++) stack. Push (I );
Foreach (int I in stack) Console. Write ("{0}", I );
Console. WriteLine ();
}
}
The output in this example is:
9 8 7 6 5 4 3 2 1 0
The statement implicitly calls the GetEnumerator method without parameters of the set to obtain an enumerator. Only one GetEnumerator method without parameters can be defined in a collection class. However, there are many ways to implement enumeration, including using parameters to control enumeration. In these cases, an iterator can be used to return the attributes and methods of the enumerated interface. For example, Stack Two new properties-IEnumerable TopToBottom and BottomToTop:
Using System. Collections. Generic;
Public class Stack : IEnumerable
{
T [] items;
Int count;
Public void Push (T data ){...}
Public T Pop (){...}
Public IEnumerator GetEnumerator (){
For (int I = count-1; I> = 0; -- I ){
Yield return items [I];
}
}
Public IEnumerable TopToBottom {
Get {
Return this;
}
}
Public IEnumerable BottomToTop {
Get {
For (int I = 0; I <count; I ++ ){
Yield return items [I];
}
}
}
}
The get accessor of the TopToBottom attribute only returns this, because the stack itself is an enumerable type. The BottomToTop attribute uses the C # iterator to return an enumeration interface. The following example shows how to use these two attributes to enumerate the elements in the stack in any order:
Using System;
Class Test
{
Static void Main (){
Stack Stack = new Stack ();
For (int I = 0; I <10; I ++) stack. Push (I );
Foreach (int I in stack. TopToBottom) Console. Write ("{0}", I );
Console. WriteLine ();
Foreach (int I in stack. BottomToTop) Console. Write ("{0}", I );
Console. WriteLine ();
}
}
Of course, these attributes can also be used outside the foreach statement. The following example passes the result of calling the property to an independent Print method. This example also shows the method body used by an iterator as a FromToBy method with parameters:
Using System;
Using System. Collections. Generic;
Class Test
{
Static void Print (IEnumerable Collection ){
Foreach (int I in collection) Console. Write ("{0}", I );
Console. WriteLine ();
}
Static IEnumerable FromToBy (int from, int to, int ){
For (int I = from; I <= to; I + = ){
Yield return I;
}
}
Static void Main (){
Stack Stack = new Stack ();
For (int I = 0; I <10; I ++) stack. Push (I );
Print (stack. TopToBottom );
Print (stack. BottomToTop );
Print (FromToBy (10, 20, 2 ));
}
}
The output in this example is:
9 8 7 6 5 4 3 2 1 0
0 1 2 3 4 5 6 7 8 9
10 12 14 16 18 20
The generic and non-generic enumerated interfaces have only one independent Member, and the GetEnumerator method without parameters returns an enumeration interface. An enumerator factory is similar to an enumerator factory ). Each time you call the GetEnumerator method of a class that correctly implements the enumeration interface, an independent enumerator is generated.
Using System;
Using System. Collections. Generic;
Class Test
{
Static IEnumerable FromTo (int from, int ){
While (from <= to) yield return from ++;
}
Static void Main (){
IEnumerable E = FromTo (1, 10 );
Foreach (int x in e ){
Foreach (int y in e ){
Console. Write ("{0, 3}", x * y );
}
Console. WriteLine ();
}
}
}
The code above prints a simple multiplication table from 1 to 10. Note that the FromTo method is called only once to generate enumeration interface e. E. GetEnumerator () is called multiple times (using the foreach statement) to generate multiple identical enumerators. These enumerators encapsulate the code specified in the FromTo declaration. Note that the from parameter is changed during iteration. However, the enumerator is independent, because for the from parameter and to parameter, each enumerator has its own copy. When you implement an enumerative class or an enumerator class, the transition between the enumerators (an unstable state) is one of the many subtle flaws that must be eliminated. The design of the iterator in C # can help eliminate these problems and implement robust enumeration classes and enumerators classes in a simple and instinctive way.
Understanding and understanding generic programming provides us with a more convenient way to write stable and secure programs. It will be an important aspect of C # development in the future and will make milestone contributions to C # And even. Net development.