. NET Surface question Series [8]-Generics

Source: Internet
Author: User

Generic type

Generics are much more grounded than reflection, delegation, and so on, and in normal work we have contact with generics almost every moment. Most people are more familiar with generics.

A generic collection is a collection of type-safe. We have type unsafe collection system.collections, relative to generic System.Collections.Generic, where members are of type object. A classic example is the ArrayList.

When using ArrayList, we can insert any type of data, and if you insert data of a value type, it will be boxed to the object type. This causes the type to be unsafe and we do not know whether the extracted data is the desired type. The data types of generics (collections) are uniform, are type-safe, have no boxing and unpacking problems, and provide better performance. Setting a default value for a generic variable is often done using the default keyword: T temp = default (t). If T is a reference type, temp is null, and if T is a value type, temp is 0.

The generic collection version of ArrayList is list<t>. T is called a type parameter. The specific type specified at the time of invocation is called the actual parameter (argument).

Interview must know the three major benefits of generics: type safety, enhanced performance, code reuse.

The use of generic collections: At almost any time, the substitution of generic collections for generic collections is not considered. Many non-generic collections also have their own generic versions, such as stacks, queues, and so on.

Generic methods

The use of generic methods can generally be used for incoming types, but they are handled in the same way. Instead of having to write many overloads, consider using a generic method to achieve code reuse. With generic constraints, you can write more rigorous methods. Generic delegates can also be seen as an application of generic methods.

For example, exchange values for two variables of the same type:

Static void Swap<t> (refref  T rhs) {    T temp;     = LHS;     = RHS;     = temp;}
Generic constraints

The purpose of a constraint is to limit the number of types that can be specified as generic arguments (that is, the specific type of T). By limiting the number of types, you can perform more operations on these types. For example, the following method, T is constrained to be a type that implements the IComparable interface. At this point, the incoming T has an additional CompareTo method in addition to the object type method. Since it is guaranteed that the incoming T must be a type that implements the IComparable interface, it is certain that the T type must contain the CompareTo method. If the constraint is removed, O1 has no CompareTo method.

Static int where T:icomparable<t>{     return  O1.compareto (O2);}

If the object type data is passed into the method, an error is displayed. Because object does not implement the Icomparable<t> interface.

Generic constraints fall into the following categories:

    • Interface constraint: A generic argument must implement an interface. Interface constraints can have more than one.
    • Base type constraint: The generic argument must be a derived class of a base class. In particular, you can specify t:class/t: struct, where T can be a reference type or a value type, respectively. The base type constraint must precede other constraints.
    • Constructor new () constraint: The generic argument must have an accessible parameterless constructor (the default is also available). The new () constraint appears at the end of the WHERE clause.

If the generic method does not have any constraints, the passed-in object is treated as an object. They are more limited in function. You cannot use the! = and = = Operators because these operators are not guaranteed to be supported by a specific type parameter.

Covariance and Contravariance

variability is the use of one object as another object in a type-safe manner. The corresponding term is invariant (invariant).

Variability

variability is the use of one object as another object in a type-safe manner. For example, variability in common inheritance: If a method declares a return type of stream, a MemoryStream can be returned when implemented. There are two types of variability: covariance and contravariance.

Covariance: You can create a variable of a more general type and assign it a value, which is a variable of a particular type. For example:

string " Test " ; //  object obj = str;

Because string is definitely an object, this change is quite normal.

Inverse degeneration: In the above example, we cannot equate Str with a new object object. If forced to do so, it can only do this:

string s = (stringnewobject();

However, this will still make an error at run time. This also tells us that the inverse degeneration is very abnormal.

Covariance and contravariance of generics

co-degeneration and out keyword, used to return the value of an action to the caller. For example, the following interface has only one method, which is to produce an instance of type T. Then we can pass in a specific type. If we can treat ifactory<pizza> as ifactory<food>. This also applies to all subtypes of food. (It will be considered a more general type of implementation)

    interface ifactory<t>    {        T CreateInstance ();    }

Inverse, in contrast, is used in conjunction with the in keyword, which means that the API will consume values rather than production values. At this point the generic type appears in the parameter:

    interface iprint<t>    {        void  Print (T value);    

This means that if we implement the IPRINT<CODE>, we can use it as iprint<csharpcode>. (It will be considered a more specific type of implementation)

If there is a bidirectional pass, nothing will happen. This type is non-variant (invariant).

     Interface Istorage<t>    {        byte[] Serialize (T value);        T Deserialize (byte[] data);    

This interface is not variant. We cannot see it as a more specific or more general type of implementation.

Assume the following inheritance relationship people–> teacher,people–> Student.

If we use it in a covariant way (assuming you've built an instance of istorage< Teacher > and treated it as istorage<people>), we might have an exception when calling serialize. Because the Serialize method does not support covariance (if the parameter is another subclass of people, such as student, istorage< Teacher > will not be able to serialize student).

If we use it in an inverse way (assuming you've built an instance of istorage<people> and treated it as istorage< Teacher >), we might have an exception when calling deserialize. Because the deserialize method does not support contravariance, it can only return people and cannot return teacher.

Using in and out to represent variability

If the type parameter is used for output, the out is used, and in if it is used for input. Note that covariance and contravariance are embodied in derived classes of generic classes T and T. Currently the out and in keywords can only be used in interfaces and delegates.

Ienumerable<out t> Support Covariance

Ienumerable<t> supports covariance, which allows a signature similar to the following

void Method (ienumerable<t> anienumberable)

Method that passes in a more specific type (the derived class of T), but inside the method, the type is treated as ienumerable<t>. Note the Out keyword.

The following example demonstrates covariance. We use ienumerable<t> covariance to pass in more specific types of circle. The compiler sees it as a more abstract type shape.

 Public classProgram { Public Static voidMain (string[] args) {            varCircles =NewList<circle>            {                NewCircle (NewPoint (0,0), the),                NewCircle (NewPoint (Ten,5), -),            }; varList =NewList<ishape>(); //covariance of generics://AddRange Incoming is a special type of list<circle>, but the requirement is generic type list<ishape>//AddRange method signature: void AddRange (ienumerable<t> collection)//ienumerable<out t> allow covariance (covariance is especially important for LINQ, because many APIs are represented as ienumerable<t>)list.            AddRange (circles); //This is the only way to do this before C # 4.0List. AddRange (circles. Cast<ishape>()); }    }     Public Sealed classCircle:ishape {Private ReadOnlyPoint Center;  PublicPoint Center {Get{returnCenter;} }        Private ReadOnly Doubleradius;  Public DoubleRadius {Get{returnradius;} }         PublicCircle (Point Center,intradius) {             This. Center =Center;  This. Radius =radius; }         Public DoubleArea {Get{returnMath.PI * RADIUS *radius;} }    }     Public InterfaceIShape {DoubleArea {Get; } }
Icomparer<in t> supports inverse degeneration

IComparer supports inverse degeneration. We can simply implement a method that compares any plot area, and the incoming input type (in) is the most general type of ishape. Then, when used, the result we get is a more specific type of circle. Because any graph can compare area, circle of course also can.

Note that the IComparer signature is public interface Icomparer<in t>.

     Public classProgram { Public Static voidMain (string[] args) {            varCircles =NewList<circle>            {                NewCircle (NewPoint (0,0), the),                NewCircle (NewPoint (Ten,5), -),            }; //the inverse of a generic type://Areacomparer can compare the area of arbitrary plots, but we can pass in specific shapes such as circles or squares//Compare Method Signature: Compare (IShape x, ishape y)//icomparer<in t> supports inversion//The incoming circle circle, but the required input is IShapeCircles. Sort (Newareacomparer ()); }    }    classAreacomparer:icomparer<ishape>    {         Public intCompare (IShape x, IShape y) {returnX.area.compareto (Y.area); }    }
Limitations of generic variability in C #

1. The variability of the type parameters of the class is not supported. only interfaces and delegates can have variable type parameters. The In and out modifiers can only be used to decorate generic interfaces and generic delegates.

2. Immutability only supports reference conversions. Immutability can only be used for reference types, prohibit any value types and user-defined conversions, as the following conversions are not valid:

    • Convert ienumerable<int> to ienumerable<object>--boxing conversion
    • Convert ienumerable<short> to ienumerable<int>--value type conversion
    • Convert ienumerable<string> to ienumerable<xname>--user-defined conversion

3. The type parameter uses out or ref to disallow variability. For generic type parameters, if you want to pass an argument of that type to a method that uses the out or ref keyword, you do not allow variability, such as:

Delegate void somedelegate< in t> (ref T T)

This code compiler will make an error.

4. The variability must be explicitly specified. From the implementation of the compiler can fully determine which generic parameters can be contravariant and covariant, but actually did not do so, because the C # development team believes that: the developer must explicitly specify the variability, because it will encourage developers to consider the consequences of their behavior, to think about their design is reasonable.

5. Multicast delegation and variability cannot be mixed. The following code can be compiled, but will throw a ArgumentException exception at run time:

func<string""; Func<objectnewobject(); Func<object> Combined = objectfunc + Stringfunc;

This is because the Delegate.combine method that is responsible for linking multiple delegates requires that the arguments must be of the same type , while the output of the two generic delegates above is a string and the other is object. The above example can be modified to the correct code as follows:

func<string""; Func<objectnew func<object>(stringfunc); Func<objectnewobject(); Func<object> Combined = objectfunc + defensivecopy;

At this point, the output of the two generic delegates is object.

The interaction between covariance and contravariance

In the following code, there is a method in interface Ibar that accepts another interface IFoo as a parameter. IFoo is a support for co-change. This can cause a problem.

    interface ifoo< in t>    {    }    interface ibar< in T>    {        void Test (ifoo<t> foo);    }

Suppose T is a string type. If there is a class of bar <t>: Ibar<t>, another class of Foo<t>:ifoo<t> an instance of bar should be able to invoke the method: Abar.test (foo).

classBar<t>: ibar<t>    {         Public voidTest (ifoo<t>foo) {            Throw Newnotimplementedexception (); }    }    classFoo<t>: ifoo<t>    {            }    classProgram { Public Static voidMain () {Bar<string> Abar =Newbar<string>(); Foo<Object> foo =Newfoo<Object>();        Abar.test (foo); }    }

When the method is called, the parameter type passed in is foo<object>. Let's look at the signature of the method:

    Interface ibar< in t>    {        void Test (ifoo<t> foo);    }

Now our Abar type parameter T is string, so the incoming type of the test method we expect should also be ifoo<string>, or it can change to a type of ifoo<string>, but it is an object. Therefore, the methods of the two interfaces are problematic.

    Interface ifoo< out t>    {    }

The problem is solved when the signature of the IFoo interface is replaced with the out modifier. At this time, due to allow the inverter,foo<object> can be changed into ifoo<string>. But I have short-sighted, and have not yet found this feature in the actual work of what application.

Resources

Http://www.cnblogs.com/LoveJenny/archive/2012/03/13/2392747.html

Http://www.cnblogs.com/xinchufa/p/3524452.html

Http://www.cnblogs.com/Ninputer/archive/2008/11/22/generic_covariant.html

. NET Surface question Series [8]-Generics

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.