A generic interface does not have a generic interface. Every time you try to use a non-generic interface (such as icomparable) to manipulate a value type, it is packed and the type security during compilation is lost. This severely limits generic applications. Therefore, CLR provides support for generic interfaces. A reference type or value type can specify a type real parameter to implement a generic interface. In addition, a type can also maintain the Unspecified status of the type real parameter to implement a generic interface. Let's take a look at some examples: the following generic interface definition was published as part of FCL: public interface ienumerable <t>: idisposable, ienumerator {T current {Get ;}} the following example implements the above generic interface and specifies the real parameter of the type: Internal sealed class triangle: ienumerator <point> {private point [] m_vertices; point Current {get {...}}} in the following example, the same generic interface is implemented, but the Unspecified status of the real parameter type is maintained: Internal sealed class arrayenumerator <t>: ienumerator <t> {private T [] m_array; // The current attribute of ienumerator <t> is T type T current {get {. .. }}} Note that an arrayenumerator object can enumerate a series of T objects (where T is not specified, allowing the code to use the generic arrayenumerator type to specify a specific type for T later ). Generic delegation CLR supports generic delegation to ensure that all types of objects can be passed to a callback method in a type-safe manner. In addition, a generic delegate allows a value-type instance to pass a callback method without any packing. The so-called delegate actually provides a class definition for four methods: A constructor, an invoke method, a begininvoke method, and an endinvoke method. If the type parameter is specified for a defined delegate type, the compiler defines the delegate class method. Assume that a generic delegate is defined as follows: Public Delegate treturn callme <treturn, tkey, tvalue> {tkey key, tvalue); the compiler converts it into a class, logically, this class can be expressed as follows: public sealed class callme <treturn, tkey, tvalue>: multicastdelegate {public callme (Object object, intptr method ); public treturn invoke (tkey key, tvalue value); Public iasyncresult begininvoke (tkey key, tvalue value, asynccallback callback, object); Public treturn en Dinvoke (iasyncresult result);} FCL provides many generic delegation types, most of which are used for set processing. The following are some examples: // It is usually used to operate a set item Public Delegate void action <t> (t obj); // It is usually used to compare two set items, public Delegate int32 comparison <t> (t x, t y ); // It is usually used to convert a set item from one type to another type public delegate toutput converter <tinput, toutput> (tinput input ); FCL also provides a generic delegate for events. The following shows the delegate: Public Delegate void eventhandler <teventargs> (Object sender, teventargs e) Where teventargs: eventargs; when a generic method defines a generic reference type, value type, or interface, any method defined in these types can reference A type parameter specified by the type. A type parameter can be used as a method parameter, as a return value of a method, or as a local variable defined within a method. However, CLR allows a method to specify its unique type parameters. These types of parameters can be used for parameters, return values, or local variables. As follows: Internal sealed class generictype <t> {private t m_value; Public generictype (T value) {m_value = value;} public toutput converter <toutput> () {toutput result = (toutput) convert. changetype (m_value, typeof (toutput); Return reault ;}} it is very interesting to use generic types for methods that accept out and ref parameters, because the variables passed as an out/Ref real parameter must have the same type as the method parameter to avoid potential damage to type security. In fact, the interlocked class exchange and compareexchange methods provide a generic version for this reason: public static class interlocked {public static t exchange <t> (ref t location, T value) where T: Class; public static t compareexchange <t> (ref t location, T value, t comparand) where T: Class ;} generic method and type interface C # Generic syntax affects code readability because it involves a large number of less than or greater than symbols. To enhance code creation, readability, and maintainability, the C # compiler supports "type inference" when calling a generic method ). That is to say, the compiler automatically determines (or derives) the type to be used when calling a generic method. Run the following code: Private Static void swap <t> (ref t O1, ref O2) {T temp = O1; O1 = O2; O2 = temp;} Private Static void callingswapusinginference () {int n1 = 1, n2 = 2; swap (ref N1, ref N2); // call swap <int> string S1 = "A"; object S2 = "B "; swap (ref S1, ref S2); // error, type cannot be deduced} A type can define multiple methods so that one method accepts a specific data type, let another method accept a generic parameter, as shown below: Private Static void display (string s) {console. writeline (s);} Private Static void display <t> (T O) {display (O. tostring (); // call display (string)} The following shows some call methods of the display method: Display ("Jeff"); // call display (string) display (123); // call display <t> (t) display <string> ("AI"); // call display <t> (t) in the C # Compiler's view, a more explicit match always takes precedence over a generic match. Therefore, display ("Jeff") generates a call to the non-generic display method. The third call to display explicitly specifies a generic type real parameter string, which tells the compiler not to try to deduce the type real parameter and directly use the specified type real parameter. In C #, attributes, indexers, events, operator methods, constructors, and finalizer cannot have type parameters. However, they can be defined in a generic type, and the code in these members can use type parameters. C # The reason why these members are not allowed to specify their own generic parameters is that the C # development team of Ms believes that other developers rarely need to use these members as generic parameters. In addition, adding code that supports generics to these members is quite high because you must design enough syntax for the language. Verifiable evidence and restrictions we can use "constraints" to restrict type variables that can be specified as generic arguments. By limiting the number of types, we can perform more operations on those types. The following is a min method, which specifies a constraint: public static T min <t> (T O1, t O2) where T: icomparable <t> {If (o1.compareto (O2) <0) return O1; return O2;} The where keyword tells the compiler that all types specified by T must implement the generic icomparable interface of the same type (t. Now, when the code references a generic type or method, the compiler is responsible for ensuring that the type arguments meet the specified constraints. For example, Private Static void callmin () {object O1 = "Jeff", O2 = "Richter"; object omin = min <Object> (O1, O2 ); // error} constraints can be applied to a generic type parameter or a generic method parameter. CLR allows overloading based on type parameter names or constraints; it can only overload types or methods based on Arity. Example: // you can define the following types of internal sealed class Atype {} internal sealed class Atype <t >{}; internal sealed class Atype <t1, T2 >{}// error: conflict with the unrestricted Atype <t> internal sealed class Atype <t> where T: icomparable <t >{} // error: And Atype <t1, t2> conflicting internal sealed class Atype <t3, T4 >{} internal sealed class anothertype {// you can define the following method: Private Static void M () {} Private Static void m <t> () {} Private Static void m <t1, T2> () {}// E Rror: conflict with unrestricted m <t> Private Static void m <t> where T: icomparable <t >{} // error: And m <t1, t2> conflicting private static void m <t3, T4 >{}} when rewriting a virtual generic method, the same number of type parameters must be specified for the overwritten versions, these type parameters inherit the constraints specified by the methods of the base class on them. In fact, a method that is rewritten cannot specify any constraints on its type parameter. However, the type parameter name can be changed. Internal class base {Public Virtual void m <t1, T2> () where t1: structwhere t2: Class {}} internal sealed class derived: Base {public override void m <t3, t4> () Where T3: eventargs // errorwhere T4: Class // error {} specify zero or one of the main constraints. The main constraint can be a reference type, which identifies an unsealed class. You do not need to specify the following reference types: system. Object, system. array, system. Delegate, system. multicastdelegate, system. valuetype, system. Enum, or system. Void. Specifying a reference type constraint is equivalent to committing to the compiler that a specified type argument is either of the same type as the constraint type or a type derived from the constraint type. For example, internal sealed class primaryconstraw.fstream <t> where T: stream {public void M (t stream) {stream. close () ;}} has two special constraints: class and struct. The class constraint promises to the compiler that a specified type real parameter is a reference type. Any class type, interface type, delegate type, or array type meets this constraint. Struct constraints promise the compiler that a specified type of real parameter is a value type, and any value type, including enumeration, meets this constraint. However, the compiler and CLR treat any system. nullable <t> value type as a special type, and the type that can be null cannot specify this constraint. The reason is that the nullable <t> type limits its type parameter to struct, while the CLR wants to disable a recursive type like nullable <t>. A secondary constraint can specify zero or multiple secondary constraints for a type parameter. A secondary constraint represents an interface type. Because multiple interface constraints can be specified, all interface constraints must be implemented for the type specified by the type arguments. There is also an auxiliary constraint called "type parameter constraint", which is also known as "Bare type constraint ". These constraints are much less used than interface constraints. It allows a generic type or method to specify that a relationship must exist between specified type parameters. One type parameter can apply zero or multiple type parameter constraints. The following example demonstrates how to use type parameter constraints: Private Static list <tbase> convertilist <t, tbase> (ilist <t> List) where T: tbase {list <base> baselist = new list <tbase> (list. count); For (INT Index = 0; index <list. count; index ++) {baselist. add (list [Index]);} return baselist;} This method specifies two types of parameters. The T parameter is subject to the tbase type parameter. That is to say, no matter what type of real parameter is specified for T, this type must be compatible with the type of real parameter specified for tbase. The following method demonstrates valid and invalid convertilist calls: Private Static void callingconvertilist () {// constructs and initializes a list <string> (which implements ilist <string>) ilist <string> ls = new list <string> (); LS. add ("A string"); // convert ilist <string> to an ilist <Object> Lo = convertilist <string, Object> (LS ); // convert ilist <string> to an ilist <icomparable> ilist <icomparable lc = convertilist <string, icomparable> (LS ); // convert ilist <string> to an ilist <icomparable <St Ring> ilist <icomparable <string> LCS = convertilist <string, icomparable <string> (LS ); // convert ilist <string> to an ilist <string> ls2 = convertilist <string, string> (LS); // error: convert ilist <string> to an ilist <exception> Le = convertilist <string, exception> (LS );} the constructor can specify zero or one constructor constraint for a type parameter. Specifying a constructor constraint is equivalent to committing to the compiler that a specified type real parameter is a non-Abstract type that implements a public non-argument constructor. NOTE: If both the constructor constraints and struct constraints are specified, the C # compiler considers this as an error because it is redundant: all value types provide a public parameter-free constructor. The following example uses the constructor constraint to restrict its type parameter: Internal sealed class constructorconstraint <t> where T: New () {public static t Factory () {return New T () ;}} it is invalid to convert a generic type variable to another type, unless it is converted to a Type compatible with a constraint: Private Static void castingagenerictypevariable <t> (t obj) {int x = (INT) OBJ; // error string S = (string) OBJ; // error int X2 = (INT) (object) OBJ; // No error string S2 = (string) (object) OBJ; // No error string S3 = OBJ as string; // No error} change a generic type It is invalid to set the generic type variable to null unless the generic type is restricted to a reference type. We can use the default keyword to set a reference type to null or a value type to 0. Private Static void setdefaultvalue <t> () {T temp = default (t);} t if it is of the reference type, the temp value is null; t if it is of the value type, the temp value is 0. Compare a generic type variable with null. Whether the generic type is restricted or not, use = or! = The operator compares a generic variable with null to be legal: Private Static void comparingwithnull <t> (t obj) {If (OBJ = NULL) {// For the value type, this will never be executed .}} If you call this method and pass a value type for the type parameter, the JIT compiler knows that the if statement will never be true, therefore, local code is not generated for the IF test or the code in both braces. If it is used again! = Operator. the JIT compiler will not generate code for the IF test (because it must be true), but will generate local code for the code in the if braces. In addition, if T is constrained into a struct, the C # compiler reports an error. Comparison of Two generic type variables if the generic type parameter is not a reference type, it is illegal to compare two variables of the same generic type. There are a lot of problems when using generic variables as operands to apply operators to them. The compiler cannot determine the type during compilation, which means that it cannot apply any operators to variables of the generic type. # Asp.net