benifit:
1.make developers extremely productive is code reuse, which is the ability to derive a class that inherits all of the capabilities of a base class.
The derived class can simply override virtual methods or add some new methods to customize the behavior of the base class to meet the developer’s needs.
2.is another mechanism offered by the common language runtime (CLR) and programming languages that provides one more form of code reuse: algorithm reuse.
such as sorting, searching, swapping, comparing, or converting
Most algorithms are encapsulated in a type, and the CLR allows the creation of generic reference types as well as generic value types.but it does not allow the creation of generic enumerated types.
3.Source code protection
The developer using a generic algorithm doesn’t need to have access to the algorithm’s source code.
With C++ templates, however, the algorithm’s source code must be available to the developer who is using the algorithm.
4.Type safety
When a generic algorithm is used with a specific type, the compiler and the CLR understand this and ensure that only objects compatible with the specified data type are used with the algorithm.
Attempting to use an object of an incompatible type will result in either a compiler error or a run-time exception being thrown.
In the example, attempting to pass a String object to the Add method results in the compiler issuing an error.
5.Cleaner code
Because the compiler enforces type safety, fewer casts are required in your source code, meaning that your code is easier to write and maintain.
In the last line of SomeMethod, a developer doesn’t need to use a (DateTime) cast to put the result of the indexer (querying element at index 0) into the dt variable.
6.Better performance
Before generics, the way to define a generalized algorithm was to define all of its members to work with the Object data type. If you wanted to use the algorithm with value type instances, the CLR had to box the value type instance prior to calling the members of the algorithm. boxing causes memory allocations on the managed heap, which causes more frequent garbage collections, which, in turn, hurt an application’s performance.
Because a generic algorithm can now be created to work with a specific value type, the instances of the value
type can be passed by value, and the CLR no longer has to do any boxing.
In addition, because casts are not necessary (see the previous bullet), the CLR doesn’t have to check the type safety of the attempted cast, and this results in faster code too.
using the generic List algorithm with the Int32 type is much faster than using the non-generic ArrayList algorithm with Int32.because using a value type (Int32) with ArrayList causes a lot of boxing operations to occur
using reference types is not as momentous.the times and number of garbage collections are about the same. So it doesn’t appear that the generic List algorithm is of any benefit here.
However, keep in mind that when using a generic algorithm, you also get cleaner code and compile-time type safety
So although the performance improvement is not huge, the other benefits you get when using a generic algorithm are usually an improvement.
其他知识:
1.Microsoft’s design guidelines state that generic parameter variables should either be called T or at least start with an uppercase T (as in TKey and TValue).
The uppercase T stands for type, just as an uppercase I stands for interface (as in IComparable).
2.the CLR generates native code for each method the first time the method is called for a particular data type. This will increase an application’s working set size, which will hurt performance
1.Generics in the Framework Class Library
1.the most obvious use.the FCL defines several generic collection classes available for your use(System.Collections.Generic namespace,System.Collections.ObjectModel namespace,System.Collections.Concurrentnamespace)
2.Microsoft recommends that programmers use the generic collection classes and now discourages use of the non-generic collection classes
(1)get the type safety, cleaner code, and better performance
(2)have a better object model.For example, fewer methods are virtual, resulting in better performance,and new members have been added to the generic collections to provide new functionality.
3.The collection classes implement many interfaces, and the objects that you place into the collections can implement interfaces that the collection classes use for operations such as sorting and searching.
The FCL ships with many generic interface definitions so that the benefits of generics can be realized when working with interfaces as well.
The commonly used interfaces are contained in the System.Collections.Generic namespace.
4.The new generic interfaces are not a replacement for the old non-generic interfaces;
in many scenarios,you will have to use both.The reason is backward compatibility.
For example, if the List<T> class implemented only the IList<T> interface, no code could consider a List<DateTime> object an IList.
5.the System.Array class, the base class of all array types, offers many static generic methods, such as AsReadOnly, BinarySearch, ConvertAll, Exists, Find, FindAll……
2.Generics Infrastructure
Generics were added to version 2.0 of the CLR, and it was a major task that required many people working for quite some time.
to make generics work, Microsoft had to do the following:
1.Create new Intermediate Language (IL) instructions that are aware of type arguments
2.Modify the format of existing metadata tables so that type names and methods with generic parameters could be expressed.
3.Modify the various programming languages (C#, Microsoft Visual Basic .NET, etc.) to support the new syntax, allowing developers to define and reference generic types and methods
4.Modify the compilers to emit the new IL instructions and the modified metadata format
5.Modify the just-in-time (JIT) compiler to process the new type-argument–aware IL instructions that produce the correct native code.
6.Create new reflection members so that developers can query types and members to determine if they have generic parameters. Also, new reflection emit members had to be defined so that developers could create generic type and method definitions at run time.
7.Modify the debugger to show and manipulate generic types, members, fields, and local variables.
8.Modify the Microsoft Visual Studio IntelliSense feature to show specific member prototypes when using a generic type or a method with a specific data type.
Open and Closed Types
how the CLR creates an internal data structure for each and every type in use by an application?
1. the CLR will create an internal type object for each of these.This applies to reference types (classes), value types (structs), interface types, and delegate types
These data structures are called type objects. Well, a type with generic type parameters is still considered a type
2.However, a type with generic type parameters is called an open type, and the CLR does not allow any instance of an open type to be constructed
3.the CLR allocates a type’s static fields inside the type object. So each closed type has its own static fields
In other words, if List<T> defined any static fields, these fields are not shared between a List<DateTime> and a List<String>;
4.if a generic type defines a static constructor, this constructor will execute once per closed type.
Sometimes people define a static constructor on a generic type to ensure that the type arguments will meet certain criteria.
5.The CLR has a feature called constraints that offers a better way for you to define a generic type indicating what type arguments are valid for it.
Unfortunately,constraints do not support the ability to limit a type argument to enumerated types only,which is why the previous example requires a static constructor to ensure that the type is an enumerated type.
how the CLR prevents an instance of an interface type from being constructed?
a type with generic type parameters is called an open type
1.When code references a generic type, it can specify a set of generic type arguments.
If actual data types are passed in for all of the type arguments, the type is called a closed type, and the CLR does allow instances of a closed type to be constructed.
2.However, it is possible for code referencing a generic type to leave some generic type arguments unspecified.
This creates a new open type object in the CLR, and instances of this type cannot be created.
the exception’s string message indicates that the type still contains some generic parameters.
the type names end with a backtick (`) followed by a number. The number indicates the type’s arity
Generic Types and Inheritance
1.A generic type is a type, and as such, it can be derived from any other type.
2.When you use a generic type and specify type arguments, you are defining a new type object in the CLR, and the new type object is derived from whatever type the generic type was derived from
In other words, because List<T> is derived from Object, List<String> and List<Guid> are also derived from Object.
Similarly, because DictionaryStringKey<TValue> is derived from Dictionary<String, TValue>, DictionaryStringKey<Guid> is also derived from Dictionary<String, Guid>.
3.specifying type arguments doesn’t have anything to do with inheritance hierarchies
示例:
1.the m_next field must refer to another node that has the same kind of data type in its m_data field.
This means that the linked list must contain nodes in which all data items are of the same type (or derived type).
2.if I use Node<Object> everywhere, but then I would lose compile-time type safety, and value types would get boxed.
3.So a better way to go would be to define a non-generic Node base class and then define a generic TypedNode class (using the Node class as a base class).
Now, can have a linked list in which each node can be of a specific data type (not Object), get compile-time type safety, and avoid the boxing of value types.
write code to create a linked list in which each node is a different data type
Generic Type Identity
1.Sometimes generic syntax confuses developers.
After all, there can be a lot of less-than (<) and greater-than (>) signs sprinkled throughout your source code, and this hurts readability
2.To improve syntax, some developers define a new non-generic class type that is derived from a generic type and that specifies all of the type arguments.
示例:
方式一:
方式二:有问题
(1)the code that creates a list can be rewritten more simply (without less-than and greater-than signs),making your source code easier to read
(2)but lose type identity and equivalence
sameType will be initialized to false
(2.1)because you are comparing two different type objects. This also means that a method prototyped as accepting a DateTimeList will not be able to have a List<DateTime> passed to it.
(2.2)However, a method prototyped as accepting a List<DateTime> can have a DateTimeList passed to it because DateTimeList is derived from List<DateTime>.
Programmers may become easily confused by all of this.
方式三:正确
C# does offer a way to use simplified syntax to refer to a generic closed type while not affecting type equivalence at all;
can use the good-old using directive at the top of your source code file
(1)the using directive is really just defining a symbol called DateTimeList.
As the code compiles, the compiler substitutes all occurrences of DateTimeList with System.Collections.Generic.List<System.DateTime>.
This just allows developers to use a simplified syntax without affecting the actual meaning of the code, and therefore, type identity and equivalence are maintained.
方式四:正确
use C#’s implicitly typed local variable feature, where the compiler infers the type of a method’s local variable from the type of the expression you are assigning to it
Code Explosion
how?
1.When a method that uses generic type parameters is JIT-compiled
the CLR takes the method’s IL,substitutes the specified type arguments, and then creates native code that is specific to that method operating on the specified data types.
This is exactly what you want and is one of the main features of generics.
2.However, there is a downside to this: the CLR keeps generating native code for every method/type combination.
This is referred to as code explosion. This can end up increasing the application’s working set substantially, thereby hurting performance
the CLR has some optimizations built into it to reduce code explosion
1.if a method is called for a particular type argument, and later, the method is called again using the same type argument, the CLR will compile the code for this method/type combination just once.
So if one assembly uses List<DateTime>, and a completely different assembly (loaded in the same AppDomain) also uses List<DateTime>, the CLR will compile the methods for List<DateTime> just once. This reduces code explosion substantially.
2.the CLR considers all reference type arguments to be identical, and so again, the code can be shared.
because all reference type arguments or variables are really just pointers (all 32 bits on a 32-bit Windows system and 64 bits on a 64-bit Windows system) to objects on the heap, and object pointers are all manipulated in the same way.
For example, the code compiled by the CLR for List<String>’s methods can be used for List<Stream>’s methods, because String and Stream are both reference types. In fact, for any reference type, the same code will be used
3.But if any type argument is a value type, the CLR must produce native code specifically for that value type.
The reason is because value types can vary in size.
And even if two value types are the same size (such as Int32 and UInt32, which are both 32 bits), the CLR still can’t share the code because different native CPU instructions can be used to manipulate these values.
3.Generic Interfaces
the ability to define generic reference and value types was the main feature of generics. However, it was critical for the CLR to also allow generic interfaces
benifit:
Without generic interfaces, any time you tried to manipulate a value type by using a non-generic interface (such as IComparable),boxing and a loss of compile-time type safety would happen again. This would severely limit the usefulness of generic types.
And so the CLR does support generic interfaces.
how?
1.A reference or value type can implement a generic interface by specifying type arguments
2.A reference or value type can implement a generic interface by leaving the type arguments unspecified
4.Generic Delegates
The CLR supports generic delegates
benifit:
1.to ensure that any type of object can be passed to a callback method in a type-safe way.
2.allow a value type instance to be passed to a callback method without any boxing
delegate :
1. is really just a class definition with four methods: a constructor, an Invoke method, a BeginInvoke method,and an EndInvoke method.
2.When you define a delegate type that specifies type parameters, the compiler defines the delegate class’s methods, and the type parameters are applied to any methods having parameters/return types of the specified type parameter.
compiling:
5.Delegate and Interface Contra-variant and Covariant Generic Type Arguments
Delegate
Each of a delegate’s generic type parameters can be marked as covariant or contra-variant.
benifit:
allows you to cast a variable of a generic delegate type to the same delegate type where the generic parameter types differ
A generic type parameter can be any one of the following:
1.Invariant
Meaning that the generic type parameter cannot be changed
2.Contra-variant
Meaning that the generic type parameter can change from a class to a class derived from it.
In C#, you indicate contra-variant generic type parameters with the in keyword.
Contra-variant generic type parameters can appear only in input positions such as a method’s argument.
3.Covariant
Meaning that the generic type argument can change from a class to one of its base classes.
In C#, you indicate covariant generic type parameters with the out keyword.
Covariant generic type parameters can appear only in output positions such as a method’s return
type.
how to use it?
1.
the generic type parameter T is marked with the in keyword, making it contra-variant; and the generic type parameter TResult is marked with the out keyword, making it covariant.
2.When using delegates that take generic arguments and return types.
it is recommended to always specify the in and out keywords for contra-variance and covariance whenever possible
because doing this has no ill effects and enables your delegate to be used in more scenarios.
示例:
if I have a variable declared as follows.
I can cast it to another Func type, where the generic type parameters are different.
Because you can pass a String to a method that wants an Object (because String is derived from Object), and because you can take the result of a method that returns an ArgumentException and treat it as an Exception (because Exception is a base class of ArgumentException), the code above compiles and is known at compile time to preserve type safety.
相关知识点:
1.Variance applies only if the compiler can verify that a reference conversion exists between types.
In other words, variance is not possible for value types because boxing would be required.
In my opinion, this restriction is what makes these variance features not that useful.
example:
can’t call it passing in a reference to a List<DateTime> object because a reference conversion doesn’t exist between the DateTime value type and Object even though DateTime is derived from Object.
solve this problem by declaring ProcessCollection as follows:add
2.the big benefit of ProcessCollection(IEnumerable<Object> collection) is that there is only one version of the JITted code.
However, with ProcessCollection<T>(IEnumerable<T>collection), there is also only one version of the JITted code shared by all Ts that are reference types.
You do get other versions of JITted code for Ts that are value types, but now you can at least call the method passing it a collection of value types.
3.variance is not allowed on a generic type parameter if an argument of that type is passed to a method by using the out or ref keyword
example:
causes the compiler to generate the following error message: Invalid variance: The type parameter ‘T’ must be invariantly valid on ‘SomeDelegate<T>.Invoke(ref T)’. ‘T’ is contravariant.
4.why they must explicitly put in or out on generic type parameters?
interface
an interface with a covariant generic type parameter
Because T is covariant, it is possible to have the following code compile and run successfully
6.Generic Methods
7.Generics and Other Members
8.Verifiability and Constraints
最新评论