| Start | ||||
|---|---|---|---|---|
| Implicit type inference (methods) When passing formal arguments to a generic method, relevant generic type arguments can usually be inferred implicitly. If all generic type can be inferred, then specifying them in the syntax is optional. Consider the following generic method. It has one formal parameter and one generic type parameter. There is a very obvious relationship between them -- the type passed as an argument to the generic type parameter must be the same as the compile-time type of the argument passed to the formal parameter. These two calls are equivalent: These two calls are also equivalent: And so are these three calls: Notice that if at least one type argument cannot be inferred, then all of them have to be specified. Consider the following generic method. The first generic type argument is the same as the type of the formal argument. But there is no such relationship for the second generic type argument. Therefore, the compiler has no way of inferring the second generic type argument in any call to this method. This doesn't work anymore: This doesn't work either, because the compiler isn't sure if we are specifying the first or the second generic parameter (both would be valid as We are required to type out both of them, like this: |
||||
| Type inference (classes) Developers can be caught out by the fact that type inference doesn't work for constructors: The first way of creating instance without explicitly specifying type parameters will cause compile time error which would say: Using the generic type 'Tuple<T1, T2>' requires 2 type arguments A common workaround is to add a helper method in a static class: |
||||
| Using generic method with an interface as a constraint type. This is an example of how to use the generic type TFood inside Eat method on the class Animal You can call the Eat method like this: In this case if you try to call: It won't be possible because the object lion does not implement the interface IFood. Attempting to make the above call will generate a compiler error: "The type 'Carnivore' cannot be used as type parameter 'TFood' in the generic type or method 'Animal.Eat(TFood)'. There is no implicit reference conversion from 'Carnivore' to 'IFood'." |
||||
| Type constraints (new-keyword) By using the The second call to to 'Bar' must be a non-abstract type with a public parameterless constructor in order to use it as parameter 'T' in the generic type or method 'Factory' There is no constraint for a constructor with parameters, only parameterless constructors are supported. |
||||
| Type constraints (classes and interfaces) Type constraints are able to force a type parameter to implement a certain interface or class. Syntax for multiple constraints: Type constraints works in the same way as inheritance, in that it is possible to specify multiple interfaces as constraints on the generic type, but only one class: Another rule is that the class must be added as the first constraint and then the interfaces: All declared constraints must be satisfied simultaneously for a particular generic instantiation to work. There is no way to specify two or more alternative sets of constraints. |
||||
| Reflecting on type parameters The |
||||
| Covariance When is an An instance of a covariant generic type with a given type parameter is implicitly convertible to the same generic type with a less derived type parameter. This relationship holds because Covariant type parameters are declared using the A type parameter declared as covariant may not appear as an input. Here's a complete example: |
||||
| Contravariance When is an An instance of a contravariant generic type with a given type parameter is implicitly convertible to the same generic type with a more derived type parameter. This relationship holds because Contravariant type parameters are declared using the A type parameter declared as contravariant may not appear as an output. |
||||
| Invariance
There is no subtype relationship for lists because you can put values into a list and take values out of a list. If If Invariant type parameters are declared by omitting both the |
||||
| Variant interfaces Interfaces may have variant type parameters. but classes and structures may not nor do generic method declarations The example below shows multiple variance declarations on the same interface |
||||
| Checking equality of generic values. If logic of generic class or method requires checking equality of values having generic type, use This approach is better than simply calling |
||||
| Type Parameters (Interfaces) Declaration: Usage (in inheritance): Usage (as the type of a parameter): |
||||
| Variant delegates Delegates may have variant type parameters. This follows from the Liskov Substitution Principle (opens new window), which states (among other things) that a method D can be considered more derived than a method B if:
Therefore the following assignments are all type safe: |
||||
| Variant types as parameters and return values If a covariant type appears as an output, the containing type is covariant. Producing a producer of If a contravariant type appears as an output, the containing type is contravariant. Producing a consumer of If a covariant type appears as an input, the containing type is contravariant. Consuming a producer of If a contravariant type appears as an input, the containing type is covariant. Consuming a consumer of |
||||
| Type Parameters (Classes) Declaration: Initialisation: Usage (as the type of a parameter): |
||||
| Type Parameters (Methods) Declaration: Invocation: There is no need to supply type arguements to a genric method, because the compiler can implicitly infer the type. However, if there is an ambiguity, generic methods need to be called with type arguemnts as |
||||
| Type constraints (class and struct) It is possible to specify whether or not the type argument should be a reference type or a value type by using the respective constraints |
||||
| Explicit type parameters There are different cases where you must Explicitly specify the type parameters for a generic method. In both of the below cases, the compiler is not able to infer all of the type parameters from the specified method parameters. One case is when there are no parameters: Second case is when one (or more) of the type parameters is not part of the method parameters: |
||||
Generic type casting Usages: |
||||
Configuration reader with generic type casting Usages: Syntax
Parameters
RemarksGenerics in C# are supported all the way down to the runtime: generic types built with C# will have their generic semantics preserved even after compiled to CIL (opens new window). This effectively means that, in C#, it is possible to reflect on generic types and see them as they were declared or check if an object is an instance of a generic type, for example. This is in contrast with type erasure (opens new window), where generic type information is removed during compilation. It is also in contrast with the template approach to generics, where multiple concrete generic types become multiple non-generic types at runtime, and any metadata required to further instantiate the original generic type definitions is lost. Be careful, however, when reflecting on generic types: generic types' names will be altered on compilation, substituting the angled brackets and the type parameters' names by a backtick followed by the number of generic type parameters. Thus, a |
Read Article
https://develop.horje.com/learn/1434/reference