C # 2.0 specification (Generics v)

Source: Internet
Author: User

Connect generic Four

20.6.5 Grammatical ambiguity

Simple names (Simple-name) and member access (member-access) in §20.9.3 and §20.9.4 are prone to grammatical ambiguity for expressions. For example, the statement

F (g<a,b> (7));


can be interpreted as a call to F with two parameters G<a and b> (7) [1]. Again, it can be interpreted as a call to F with a parameter, which is a call to a generic method G with two type arguments and a formal argument.
If the expression can be parsed into two different valid methods, then when ">" can be parsed as all or part of the operator, or as a list of type arguments, then the tag immediately following ">" will be checked. If it is one of the following:

{}] >:; , . ?


Then ">" is parsed as a type argument list. Otherwise, ">" is parsed as an operator.

20.6.6 using generic methods for delegates

An instance of a delegate can be created by referencing the declaration of a generic method. The exact compile-time processing of delegate expressions, including delegate-creation expressions that reference generic methods, is described in §20.9.6.
When a generic method is invoked through a delegate, the type that is used is arguments determined when the delegate is instantiated. Type arguments can be either explicitly given through a type argument list or determined by type inference (§20.6.4). If type inference is used, the argument type of the delegate is used as the argument type of the inference process. The return type of the delegate is not used for inference. The following example shows a method that provides type arguments for a delegate instantiation expression.

delegate int D (string s, int i) delegate int E (); class X{public static T f<t> (string s, T t) {...} public static T g<t> () {...} static void Main () {d D1 = new D (f<int>),//ok, type argument is explicitly given D D2 = new D (F),//ok,int is inferred as a type argument e e1 = new E (g<int>) ; OK, the type argument is explicitly given an e e2 = new E (G); Error, cannot infer from return type}}

In the previous example, a non-generic delegate type was instantiated using a generic method. You can also use a generic method to create an instance of a constructed delegate type. In all cases, when a delegate instance is created, the type argument is given or can be inferred, but when the delegate is invoked, the type argument list (§15.3) can be used.

20.6.7 non-generic properties, events, indexers, or operators

Properties, events, indexers, and operators they can have no type arguments themselves (although they can appear in a generic class and can use type arguments from an enclosing class). If you need a widget that resembles an attribute generic, instead you have to use a generic method.

20.7 constraints

Generic types and method declarations can optionally specify a type parameter constraint, which can be done by including a type parameter constraint statement in the declaration.

type-parameter-constraints-clauses (type parameter constraint statement:) type-parameter-constraints-clause (type parameter constraint Statement) type-parameter-constraints-clauses type-parameter-constraints-clause (type parameter constraint statement Type parameter constraint statement) Type-parameter-constraints-clause: (type parameter constraint statement:) where Type-parameter:type-parameter-constraints (where Type parameter: type parameter constraint) Type-parameter-constraints: (type parameter constraint:) Class-constraint (class constraint) interface-constraints (interface Constraint) Constructor-constraint (constructor constraint) Class-constraint, Interface-constraints (class constraint, interface constraint) Class-constraint, Constructor-constraint (class constraint, constructor constraint) interface-constraints, Constructor-constraint (interface constraints, constructor constraints) Class-constraint, Interface-constraints, Constructor-constraint (class constraints, interface constraints, Constructor constraint) Class-constraint: (class constraint:) Class-type (class type) Interface-constraint: (interface constraint:) Interface-constraint (interface constraint) interface-constraints, interface-constraints (interface constraint, interface constraint) Interface-constraints: (interface constraint:) Interface-type (interface type) Constructor-constraint: (constructor constraint:) New () 

The

Each type parameter constraint statement consists of the name of the where immediately following the type parameter, followed by the colon and the constraint list of the type parameter. There can be only one where statement for each type parameter, but the where statement can be listed in any order. Similar to the get and set flags in property accessors, where statements are not keywords. The list of constraints given by

in the where statement can contain the following components in this order: a single class constraint, one or more interfaces, and a constructor constraint, new ().
If the constraint is a class type or an interface type, this type specifies the minimum "base type" of each type argument that the type parameter must support. Whenever a constructed type or generic method is used, the constraint on the type argument is checked at compile time. The provided type arguments must be either sent or implemented for all constraints that are determined by that type parameter. The
is specified as a class-constrained type and must follow the rules below.

The type must be a class type.
The type must be sealed (sealed). The
type cannot be of type: system.array,system.delegate,system.enum, or System.ValueType type.
The type cannot be an object. Because all types derive from Object, this constraint will have no effect if allowed.
at most, a constraint on a given type parameter can be a class type. The type specified by

as an interface constraint must meet the following rules.

The type must be an interface type.
The same type cannot be specified more than once in a given where statement.

In many cases, a constraint can contain a type parameter of any associated type, or a method declaration as part of a constructed type, and can include a declared type, but the constraint cannot be a single type parameter. The
is specified as any class or interface type that is constrained as a type parameter, and must be at least accessible (§10.5.4) as a generic type or declared method.
If the WHERE statement of a type parameter includes a constructor constraint in the form of new (), it is possible to use the new operator to create an instance of that type (§20.8.2). Any type argument that is used for a type parameter with a constructor constraint must have a parameterless constructor (see §20.7 for details).
The following are examples of possible constraints

Interface Iprintable{void Print ();} Interface Icomparable<t>{int CompareTo (T value);} Interface Ikeyprovider<t>{t GetKey ();} Class printer<t> where t:iprintable{...} Class sortedlist<t> where t:icomparable<t>{...} Class Dictionary<k,v>where K:icomparable<k>where:v: Iprintable,ikeyprovider<k>,new () {...}

The following example is an error because it attempts to use a type parameter directly as a constraint.

Class Extend<t, u> where u:t{...} Error


The value of the type parameter type of the constraint can be used to access the instance members that the constraint implies. In the example

Interface Iprintable{void Print ();} Class printer<t> where T:iprintable{void Printone (T x) {x.pint ();}}

The Iprintable method can be called directly on X, because T is constrained to always implement iprintable.

20.7.1 following constraints

Whenever you use a constructed type or a reference to a generic method, the supplied type arguments are checked against the type parameter constraints declared on the generic type or in the method. For each where statement, type argument a corresponding to the named type parameter is checked against each constraint as follows.


If the constraint is a class type or an interface type, let C represent a constraint that provides the type argument, which arguments overrides any type parameter that appears in the constraint. In order to follow the constraints, type a must not be converted to type C as follows:

-Same conversion (§6.1.1)
-Implicit reference conversion (§6.1.4)
-Boxing conversion (§6.1.5)
-Implicit conversions from type parameters A to C (§20.7.4).

If the constraint is new (), the type argument a cannot be abstract and must have a public parameterless constructor. If one of the following is true, this will be met.

-A is a value type (as described in §4.1.2, all value types have a public default constructor).
-A is a non-abstract class, and a contains an argument-free public constructor.
-A is a non-abstract class and has a default constructor (§10.10.4).
If the constraints of one or more type parameters are not met by a given type argument, a compile-time error occurs.
Because the type parameter is not inherited, the same constraint is never inherited. In the following example, D must specify a constraint on its type parameter T to satisfy the constraint imposed by the base class b<t>. Instead, Class E does not need to specify a constraint, because for any t,

The list<t> implements the IEnumerable interface. Class b<t> where t:ienumerable{...} Class d<t>:b<t> where t:ienumerable{...} Class e<t>:b<list<t>>{...}

20.7.2 member lookups on type parameters

In the type given by the type parameter T, the result of the member lookup depends on the constraint (if any) that is specified for T. If T has no constraints or only a new () constraint, a member lookup on T, like a member lookup on an object, returns a set of identical members. Otherwise, the first stage of the member lookup takes into account all members of each type that the T is constrained to, the result will be merged, and the hidden member will be removed from the merge results.

Before generics occur, member lookups always return a set of members that are uniquely declared in a class, or a set of members that are uniquely declared in an interface, or it can be of type object. The member lookup on the type parameter has made some changes. When a type parameter has a class constraint and one or more interface constraints, member lookups can return a set of members, some of which are declared in the class, and others that are declared in the interface. The following additional rules deal with this situation.

In the member lookup process (§20.9.2), a member declared in a class other than object hides the member declared in the interface.
During overload resolution of methods and indexers, if any of the available members are declared in a class different from object, all members declared in the interface are removed from the considered member collection.


These rules are valid only if you bind a class constraint and an interface constraint to a type parameter. The popular argument is that members defined in a class constraint are always preferred for members that are constrained by the interface.

20.7.3 type parameters and boxing

When a struct type overrides a virtual method that inherits from System.Object (Equals, GetHashCode, or ToString), calling the virtual method through an instance of the struct type will not result in boxing. This is true even when a struct is used as a type parameter and is called by an instance of the type parameter type. For example

Using System;struct counter{int value;public override string ToString () {Value++;return value. ToString ();}} Class program{static void Test<t> () where t:new () {T x = new T (); Console.WriteLine (X.tostring ()); Console.WriteLine (X.tostring ()); Console.WriteLine (X.tostring ());} static void Main () {test<counter> ();}}

The output of the program is as follows

123

Although it is recommended not to have ToString with an additional effect (side effect) [2], this example illustrates the three x. The call to ToString () does not take place in boxing.
Boxing never occurs implicitly when a member is accessed on a type parameter of a constraint. For example, suppose an interface ICounter contains a method increment that can be used to modify a value. If ICounter is used as a constraint, the implementation of the Increment method is invoked through a reference to the variable called by increment, which is not a boxed copy.

Using System;interface icounter{void Increment ();} struct Counter:icounter{int value;public override string ToString () {return value. ToString ();} void Icounter.increment () {value++;}} Class program{static void Test<t> () where t:new, icounter{t x = new T (); Console.WriteLine (x); x.increment (); Modify X ' Console.WriteLine (x); ((icounter) x). Increment (); Modify the boxed copy of x Console.WriteLine (x);} static void Main () {test<counter> ();}}

The first call to the variable x increment modifies its value. This is not equivalent to the second call to increment, and the second modification is the X-boxed copy, so the output of the program is as follows

20.7.4 conversions that contain type parameters

The conversions that are allowed on the type parameter T depend on the constraints specified for T. All constrained or non-constrained type parameters can have the following conversions.

The implicit same conversion from T to T.
An implicit conversion from t to object. At run time, if T is a value type, this is done through a boxing conversion. Otherwise, it will act as an implicit reference to the transformation.
An implicit conversion from object to T. At run time, if T is a value type, this is done through an unboxing operation. Otherwise, it will be converted as an explicit reference.
An explicit conversion from T to any interface type. At run time, if T is a value type, this is done through a boxing conversion. Otherwise, it will be done through an explicit reference conversion.
An implicit conversion from any interface type to T. At run time, if T is a value type, this is done through an unboxing operation. Otherwise, it will be converted as an explicit reference.


If the type parameter T specifies an interface I as the constraint, the following additional conversion exists.

The implicit conversion from T to I, and the conversion of any base interface type from T to I. At run time, if T is a value type, this will be done as a boxing conversion. Otherwise, it will be used as an implicit reference conversion.


If the type parameter T specifies type C as a constraint, the following additional conversions exist:

Implicit reference conversions from T to C, classes derived from T to any C, and from T to any interface from which it is implemented.
Explicit reference conversions from C to T, classes derived from C from [3] to T, and any interfaces implemented by C to T



If there is an implicit user-defined conversion from C to a, an implicit user-defined conversion from T to a.
If there is an explicit user-defined conversion from A to C, an explicit user-defined conversion from a to T.
Implicit reference conversions from null type to T

An array type T with an element type has a reciprocal conversion (§6.1.4,§6.2.3) between the object and the System.Array. If T has a class type specified as a constraint, there will be additional rules as follows

An implicit reference conversion from an array type with an element type T to an array type au with an element type U, and an explicit reference conversion from AU to at, if both of the following are true:

-At and AU have the same number of dimensions.
-U is one of these: C,c is derived from the type, the interface implemented by C, as the constraint on T and the specified interface I, or I's base interface.
It may be a bit odd that the previous rules do not allow direct implicit conversions from non-constrained type parameters to non-interface types. The reason for this is to prevent confusion and make the semantics of the conversion clearer. For example, consider the following declaration.

Class X<t>{public static long F (T t) {return (long) T;//OK, allow conversion}

}
If the direct explicit conversion of T to int is allowed, you may easily think of x<int>. F (7) will return 7L. But not really, because the standard numeric conversions are only considered when the type is known at compile time. To make the semantics clearer, the previous example must be written as follows.

Class X<t>{public static long F (T t) {return (long) (object) T;//ok; allow conversion}}

[1] In this case ">" is interpreted as greater than the operator.

[2] When overriding ToString in the program, it is generally not recommended to add this kind of computational logic, because it is not easy to control the results of this change, increasing the complexity of the debug program.

[3] The base class of C or its base class, etc.

The above is C # 2.0 specification (generics five) content, more relevant content please pay attention to topic.alibabacloud.com (www.php.cn)!

  • Related Article

    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.