Introduction:
This topic is mainly responsible for continuing to introduce other generic content in the previous topic. I will not talk about it here, so I will go directly to the content of this topic.
I. type inference
When writing generic code, we often have a large number of "<" and ">" symbols, so sometimes there are more than one code, it is inevitable that developers will feel a little dizzy when reading the Code. At this time, we will definitely think like this when we feel dizzy: can I save some "<" and ">" symbols? If you have such a demand, of course, Microsoft's good guy will certainly help you solve the problem, so that we can have this part of content --Type inferenceThis means that the compiler automatically determines the type to be used when calling a generic method. (Note that type inference only applies to generic methods, not applicable to generic type). The following is the Demo code:
Using system; namespace type inference example {class program {static void main (string [] ARGs) {int n1 = 1; int n2 = 2; // code to be written when no type inference exists // genericmethodtest <int> (ref N1, ref N2 ); // code to be written after data type inference // at this time, the compiler can determine whether to use the int type real parameters to call the generic method based on the passed real parameters 1 and 2. // it can be seen that there are <>, in this way, the readability of genericmethodtest (ref N1, ref N2) can be enhanced when there are many codes; console. writeline ("N1 value:" + N1); console. writeline ("N2 value:" + N2); console. read (); // string T1 = "12 3 "; // object t2 =" 456 "; // The compilation fails at this time. The data type of the variable cannot be inferred, instead of referencing the object's data type using variables, the following code will fail because the C # compiler finds that T1 is a string, t2 is an object type // even if T2 references a string, the compiler cannot deduce the type because t1 and t2 are different data types, so an error is returned. // Genericmethodtest (ref T1, ref T2);} // demo Private Static void genericmethodtest for type inference <t> (ref t T1, ref t T2) {T temp = T1; t1 = t2; t2 = temp ;}}}
Detailed comments are provided in the Code, which will not be explained here.
Ii. Type Constraints
If you read my previous topic, you will notice that I used where T: icomparable when implementing generic classes, in the previous topic, I didn't tell you about the usage of this generic type. This usage is the type constraint to be discussed in this section. In fact, the where T: icomparable code is also very understandable, you can also guess (if I don't know it, it should be that the guess type parameter T must meet the icomparable interface condition, because where indicates what conditions are met, however, this is true.) let's take a look at the constraints of type parameters in generics. First, when compiling generic code, the C # compiler will certainly analyze the Code. If we define a generic method as follows, the compiler will report an error:
// Compare the two numbers and return the large private static T max <t> (T obj1, t obj2) {If (obj1.compareto (obj2)> 0) {return obj1;} return obj2 ;}
If a generic method is defined as above, the C # compiler will prompt an error message: "T" does not contain the definition of "compareto, the Extension Method "compareto" for the first parameter of the acceptable type "T" cannot be found ". This is because the type parameter t can be of any type, but many types do not provide the compareto method, so the C # compiler cannot compile the above Code (The same is true for compilers.) Will definitely think -- if the C # compiler knows that the type parameter T has the compareto method, then the above Code will pass the verification by the C # compiler, there will be no compilation errors (the C # compiler is very user-friendly and will solve the problem according to the way people think, because the compiler is also developed by people, of course it will be humanized, because developers thought that at the time, they wrote the logic to the implementation of the compiler.) This made us want to make certain constraints on the type parameters, narrowing down the number of types represented by type parameters -- this is the purpose of our type constraints, and it is natural to haveType parameter Constraints(Here, we analyze the problems we encounter and find a solution to introduce the concept of type constraints. The main reason is that the language features in C # are proposed, this is not to say that Microsoft wants to propose it. It is mainly because users have such requirements. In this way, I think we can better understand the development process of C # language features, this gives me a better understanding of C #. I have also learned from my previous topics how to introduce the problem. However, this is my personal understanding, I hope this method will help you better understand the C # language features. If you have any comments or suggestions, you can leave a comment, if you think it is good, you may have to acknowledge it.). Therefore, the code above can specify a type constraint so that the C # compiler knows that this type parameter will certainly have a compareto method, so that the compiler will not report an error, we can change the above Code to (in the code T: icomparable <t> for the type parameter T, all the real parameters of the type must be generic.Icomparable Interface):
// Compare the two numbers and return the large private static T max <t> (T obj1, t obj2)Where T: icomparable <t>{If (obj1.compareto (obj2)> 0) {return obj1;} return obj2 ;}
Type constraints are usedWhereKeyword to limitParameter of the specified typeNumber of types, as shown in the preceding where T: icomparable <t> statement. C # has four constraints that can be used. However, the syntax of these four constraints is similar. (Constraints should be placed at the end of the generic method or generic type declaration, and the where keyword should be used)
(1) reference type constraints
The representation isT: ClassMake sure that the passed type parameter must be of the reference type (note that the type parameter of the constraint has nothing to do with the type itself, that is, when defining a generic struct, the generic type can be constrained to the reference type. In this case, the struct type itself is a value type, while the type parameter constraint is a reference type), which can be any class, interface, delegate, or array; however, note that the following special reference types cannot be specified:System. Object, system. array, system. Delegate, system. multicastdelegate, system. valuetype, system. Enum, and system. Void.
As defined below:
using System.IO; public class samplereference<T> where T : Stream { public void Test(T stream) { stream.Close(); } }
In the above Code, the type parameter t sets the reference type constraint,Where T: StreamIndicates that the passed type real parameter must be system. Io. stream or a type derived from stream. If a type parameter has no constraints specified, the default value T isSystem. ObjectType (equivalent to a default constraint. If no constructor is specified for each class, there will be a default non-parameter constructor. If a constructor with parameters is specified, the compiler will not generate a default constructor ). However, if we display the specified system. Object constraint in the Code, the compiler reports an error:The constraint cannot be a special class "object "."(Here you can try it yourself)
(2) value type constraints
The representation isT: structMake sure that the value type of the passed type real parameter includes enumeration, but can be empty type exclusion (the null type will be introduced in the topic below), as shown in the following example:
// Value Type constraint public class samplevaluetype <t> where T: struct {public static t test () {return New T ();}}
In the code above,New T ()Yes, it can be compiled because T is a value type, and all value types have a common no-argument constructor. However, if t is not restricted, or when the constraint is of the reference type, the above Code will report an error because some reference types do not have public constructors without parameters.
(3) constructor type constraints
The representation isT: New (),If the type parameter has multiple constraints, this constraint must be specified at the end. Make sure that the specified type real parameter has a non-Abstract type of the common no-argument constructor, which applies to: All value types; all non-static, non-abstract, and non-declared constructor classes (as mentioned in the brackets above, if the declared constructor with parameters is displayed, then, the compiler will not generate a default no-argument constructor for the class. You can view it through the Il disassembly program, so we will not map it here ); displays all non-abstract classes that declare a common no-argument constructor. (Note: If the constructor constraints and struct constraints are specified at the same time, the C # compiler will regard this as an error because such a designation is redundant, all value types are implicitly provided with a public constructor without parameters. Just like defining an interface to set the access type to public, the compiler reports an error because the interface must be public, this is redundant, so an error is reported .)
(4) Conversion Type Constraints
The representation isT: Base Class Name(Ensure that the specified type real parameter must be a base class or a subclass derived from the base class)Or T: Interface Name(Ensure that the specified type of real parameter must be an interface or a class that implements this interface)Or T: u(The type parameter provided for T must be a parameter provided for U or derived from a parameter provided for u ). An example of the conversion constraint is as follows:
Statement |
Examples of constructed types |
Class sample <t> where T: Stream |
Sample <stream> valid The sample <string> is invalid. |
Class sample <t> where T: idisposable |
Sample <stream> valid The sample <stringbuilder> is invalid. |
Class sample <t, u> where T: u |
Sample <stream, idispsable> valid The sample <string, idisposable> is invalid. |
(5) combination constraints (the fifth constraint is the combination of the first four constraints)
When multiple constraints are combined, they are combined. (Note: no real-time reference type is a value type, so reference constraints and value constraints cannot be used at the same time.) If there are multiple Conversion Type constraints, if one of them is a class, the class must be placed before the interface. Different types of parameters can have different constraints, but they must be separated by a separate where keyword. Let's take a look at some valid and invalid examples to help you better understand them:
Valid:
Class sample <t> where T: Class, idisposable, new ();
Class sample <t, u> where T: class where u: struct
Invalid:
Class sample <t> where T: Class, struct (there is no real-time reference type and value type, so it is invalid)
Class sample <t> where T: stream, class (the reference type constraint should be the first constraint, put at the beginning, so it is invalid)
Class sample <t> where T: New (), stream (constructor constraints must be placed at the end, so it is invalid)
Class sample <t> where T: idisposable, stream (the class must be placed before the interface, so it is invalid)
Class sample <t, u> where T: struct where u: Class, T (type parameter "T" has the "struct" constraint, therefore, "T" cannot be used as the "U" constraint, so it is invalid)
Class sample <t, u> where T: stream, u: idisposable (different types of parameters can have different constraints, but they need a separate where keyword, so it is invalid)
Iii. Calling generic Methods Using Reflection
The following is an example of how to use reflection to dynamically call a generic method (reflection content can be found in my blog article: http://www.cnblogs.com/zhili/archive/2012/07/08/AssemblyLoad_and_Reflection.html), the Demo code is as follows:
Using system; using system. reflection; namespace reflectiongenericmethod {class program {static void main (string [] ARGs) {test = new test (); Type type = test. getType (); // first, obtain the method definition. // If the bindflags real parameter is not input, the getmethod method returns only public members. // here I specify nonpublic, that is, the private member is returned. // (if public or nonpublic is specified, you must specify instance | static at the same time. Otherwise, no members are returned, you can use the code to test it.) methodinfo methodefine = type. getmethod ("printtypeparametermethod", bindingflags. nonpublic | bindingflags. instance | bindingflags. static); methodinfo constructed; // use the makegenericmethod method to obtain a constructed generic method constructed = methodefine. makegenericmethod (typeof (string); // call constructed for a generic method. invoke (null, null); console. read () ;}} public class test {Private Static void printtypeparametermethod <t> () {console. writeline (typeof (t ));}}}
When the code above calls a generic method, the two arguments passed in are both null. The first one is null because a static method is called, the second null is because the called method is a method without parameters. Running result (the result is the type of the output type real parameter, and the result is the same as we expected ):
Iv. Summary
Here, all the generic content has been introduced. This series uses three topics to introduce generics. The content of this article is based on questions (why is there a wildcard) to explain the questions, further, I have a deep understanding of the generic mode (I personally think this is a good way to explain it. If you have a better way to explain it, I can leave a message below ), we hope this method will let everyone know the origins of generics and better understand generics. The next topic will introduce the improvements to generics in C #4.0 --Generic Variability.
Source code for all demos used in generic topics: http://files.cnblogs.com/zhili/GeneralDemo.zip