Disadvantages of non-generic containers:
(1) Performance issues.
When using a value type, the value type must be boxed (Boxing) for push and store and unboxed (unboxing) When the value type is removed from the container. Boxing and unboxing can cause significant performance damage based on the permissions of the value type. Also, boxing and unboxing also increase the pressure on the managed heap, resulting in more garbage collection work, which is not good for performance. Even when you use a reference type instead of a value type, there is a performance penalty because you must forcibly convert the object to the actual type you want, causing the type conversion overhead to be enforced, and the code is as follows:
Container C = new Container ();
C.insert ("1");
String number = (string) c.delete ();
(2) Type safety.
Type conversions are difficult to guarantee that each conversion succeeds, which causes some errors to be checked at compile time and exception at run time. Compile-time type safety is lost because the compiler allows coercion of type conversions between any type and object.
(3) Work efficiency.
Writing type-specific data structures is a tedious, repetitive, and error-prone task. Also, the use of unknown types cannot be predicted, so you must maintain an object-based data structure.
Complete C # implementations of generic containers
1 Public classContainer<t>2 {3 ReadOnly intm_size;4 Private intM_containerpointer;5 t[] M_items;6 7 PublicContainer ()8: This( -)9 {TenM_containerpointer =-1; OneM_items =Newt[ -]; A } - PublicContainer (intsize) - { theM_size =size; -M_containerpointer =-1; -M_items =NewT[size]; - } + Public intCount - { + Get{returnM_containerpointer;} A } at Public BOOLIsEmpty - { - Get{returnM_containerpointer = =-1; } - } - Public BOOLIsfull - { in Get{returnM_containerpointer = = M_size-1; } - } to Public voidInsert (ObjectItem//inserts an element at the end of a container + { - if(isfull) the { *Console.WriteLine ("Container is full!"); $ return;Panax Notoginseng } -M_items[++m_containerpointer] =(T) item; the } + Public ObjectDelete ()//remove an element from the end of a container A { the if(M_containerpointer >=0) + { - returnm_items[m_containerpointer--]; $ } $ return NULL; - } -}
On the surface, the syntax for C # generics looks similar to C + + templates, but there are important differences in how the compiler implements and supports them. C # Generics provide enhanced security compared to C + + templates, but are somewhat limited in functionality. In some C + + compilers, the compiler does not even compile template code until a template class is used by a particular type. When you do specify a type, the compiler inserts the code inline and replaces each occurrence of a generic type parameter with the specified type. In addition, each time a particular type is used, the compiler inserts code specific to that type, regardless of whether the type has been specified for the template class at some other point in the application. The C + + linker is responsible for resolving the problem and is not always valid. This can lead to code bloat, which increases the load time and memory footprint. In the. NET Framework 2.0, generics have native support in IL (intermediate language) and the CLR itself. When compiling generic C # code, the compiler compiles it to IL, just like any other type. However, IL contains only parameters or placeholders of the actual specific type, and has a dedicated IL directive support
Generic operation. Generic code contains generic information in the metadata. A true generic instantiation works in a "on-demand" manner, which occurs at JIT compilation time. When JIT-compiled, the JIT compiler substitutes the specified type arguments for T in the generic IL code metadata for the instantiation of the generic type. This provides the JIT compiler with type-specific IL metadata definitions, as if generics were never involved. This enables the JIT compiler to ensure the correctness of method parameters, implement type safety checks, and even perform type-specific IntelliSense. When. NET compiles generic IL code to native code, the resulting native code depends on the specified type. If the machine specifies a value type, the JIT compiler replaces the generic type parameter with a specific value type and compiles it to native code. The JIT compiler tracks the type-specific IL code that has been generated. If the JIT compiler compiles a generic IL code with a value type that has been compiled to native code, it simply returns a reference to that IL code. Because the JIT compiler will use the same value type-specific IL code in all future scenarios, there is no code bloat problem. If the machine specifies a reference type, the JIT compiler replaces the generic parameter in the generic IL code with object and compiles it to native code. This code will be used in any future request for a reference type other than a generic type parameter. The JIT compiler will only reuse the actual code. Instances still allocate space according to their size away from the managed heap, and no type conversions are enforced.
Benefits of Generics
Generics enable code to be reused, and types and internal data can be changed without causing code bloat, regardless of value type or reference type. You can develop, test, and deploy code at once, reuse it through any type, including future types, and all with compiler support and type safety. Performance is significantly improved because generic code does not force boxing and unboxing of value types, or downward-forcing type conversions on reference types. For value types, performance typically increases by 200%, and for reference types, you can expect performance to increase by up to 100% when accessing the type (of course, the performance of the entire application may or may not improve).
C # generic class container