C # 2.0 specification (Generic v)

Source: Internet
Author: User
Tags abstract constructor expression modify tostring valid


Connect the generic type four
20.6.5 Grammatical ambiguity
Simple names (Simple-name) and member access (member-access) in §20.9.3 and §20.9.4 can easily cause syntactic ambiguity in 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, a generic method G with two type arguments and a formal argument.
If an expression can be resolved to two different valid methods, the tag immediately following ">" will be checked when ">" can be resolved as all or part of the operator or as a list of type arguments. If it is one of the following:
{}] >:; , . ?
Then ">" is parsed as a list of type arguments. Otherwise, ">" is parsed as an operator.
20.6.6 using generic methods on delegates
An instance of a delegate can be created by referencing the declaration of a generic method. The exact compile-time processing of a delegate expression, including a delegate-creation expression referencing a generic method, is described in §20.9.6.
When a generic method is invoked by a delegate, the type real arguments used is determined when the delegate is instantiated. Type arguments can be given explicitly by type argument lists, or by type inference (§20.6.4). If type inference is used, the parameter type of the delegate is used as the argument type of the inferred processing. The return type of the delegate is not used for inference. The following example shows a method that provides a type argument for a delegate-instantiated 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 arguments are explicitly given
D d2 = new D (F); Ok,int is inferred as a type argument
E e1 = new E (g<int>); OK, type arguments are explicitly given
e e2 = new E (G); Error, cannot infer from return type
}
}
In the previous example, Non-generic delegate types are instantiated using generic methods. 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, it is not possible to provide a type argument list (§15.3).


20.6.7 non-generic properties, events, indexers, or operators
Properties, events, indexers, and operators themselves can have no type arguments (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, you have to use a generic method instead.
20.7 constraints
Generic types and method declarations can optionally specify type parameter constraints, which can be done by including the type parameter constraint statements 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 constraints)
Class-constraint: (class constraint:)
Class-type (class type)
Interface-constraint: (interface constraint:)
Interface-constraint (interface Constraint)
Interface-constraints, Interface-constraints (interface constraints, interface constraints)
Interface-constraints: (interface constraint:)
Interface-type (interface type)
Constructor-constraint: (constructor constraint:)
New ()
Each type parameter constraint statement consists of a label where the name of the type parameter is immediately followed by a list of constraints on the colon and type parameters. 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 the property accessor, the where statement is not a keyword.


The list of constraints given in the where statement can contain the following components in this order: a single class constraint, one or more interface approx, and 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 supplied type argument must be born or implemented with all constraints of that type parameter.
A type that is specified as a class constraint must follow the following rules.
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 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 as an interface constraint must satisfy 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 association type or a method declaration as part of a constructed type, and can include a declared type, but a constraint cannot be a single type parameter.
Any class or interface type that is specified as a type parameter constraint, either as a generic type or as a declared method, must be at least accessible (§10.5.4).
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 an 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 member of the constraint hint. 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 Follow constraints
Whenever you use a constructed type or reference a generic method, the supplied type arguments are checked against the type parameter constraints declared in the generic type, or in the method. For each where statement, type argument a, which corresponds to the named type parameter, is checked against each of the following constraints.



If the constraint is a class type or an interface type, let C represent a constraint that provides the type argument, and the type real arguments replaces any type parameter that appears in the constraint. In order to follow 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)
-An implicit conversion from type parameter A to C (§20.7.4).
If the constraint is new (), 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 a parameterless public constructor.
-A is a non-abstract class and has a default constructor (§10.10.4).
A compile-time error occurs if the constraint of one or more type parameters is not satisfied with the given type argument.
Because type parameters are not inherited, the same constraints are never inherited. In the following example, D must specify a constraint on its type parameter T to satisfy the constraints imposed by the base class b<t>. Instead, Class E does not need to specify a constraint because the IEnumerable interface is implemented for any t,list<t>.
Class b<t> where t:ienumerable{...}
Class d<t>:b<t> where t:ienumerable{...}
Class e<t>:b<list<t>>{...}

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


Before generics appear, member lookup always returns a set of members that are uniquely declared in a class, or a group of members that are uniquely declared in an interface, or type object. Some changes have been made to the member lookup on the type parameter. When a type parameter has a class constraint and one or more interface constraints, a member lookup can return a set of members, some of which are declared in the class, and some are declared in the interface. The following additional rules deal with this situation.
In a member lookup procedure (§20.9.2), a member declared in a class other than object hides the member declared in the interface.
In the overload resolution of methods and indexers, if any of the available members are declared in a class that is different from object, all members declared in the interface are removed from the set of members being considered.

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 bound by an 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), invoking the virtual method through an instance of the struct type will not result in boxing. This is true even when the struct is used as a type parameter and the invocation takes place through 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
1
2
3
Although it is recommended that ToString not have an additional effect (side effect) [2], this example shows that for three x. The call to ToString () does not occur boxing.
Boxing never implicitly occurs when a member is accessed on the 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 a variable called on it 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 increment, and the second modification is the X-boxed copy, so the program's output is as follows
20.7.4 contains the conversion of the type parameter
The conversion allowed on the type parameter T depends on the constraint specified for T. All constrained or unconstrained 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 will be done through a boxing conversion. Otherwise, it will be converted as an implicit reference.
An implicit conversion from object to T. At run time, if T is a value type, this is done through a 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 will be done through a boxing conversion. Otherwise, it will be done through an explicit reference to the conversion.
An implicit conversion from any interface type to T. At run time, if T is a value type, this is done through a unboxing operation. Otherwise, it will be converted as an explicit reference.

If the type parameter T specifies an interface I as a constraint, the following additional conversions will exist.
An implicit conversion from T to I, and a conversion from 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 made as an implicit reference to the conversion.

If type parameter t specifies type C as a constraint, the following additional conversions exist:
Implicit reference conversions from T to C, from T to any C-derived class, and from T to any interface implemented from it.
An explicit reference conversion from C to T, classes derived from C [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 types 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 that is 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 if the following two are true, there will be an explicit reference conversion from AU to at:
-At and AU have the same number of dimensions.
-U is one of these: the type that c,c derives from, the interface implemented by C, the interface I, or the base interface of I, specified as a constraint on T.
It may seem odd that previous rules do not allow direct implicit conversions from unconstrained type parameters to non-interface types. The reason for this is to prevent confusion and to make the semantics of this transformation clearer. For example, consider the following statement.
Class x<t>
{
public static long F (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 it is not, because the standard numeric conversion is only considered when the type is known at compile time. To make the semantics clearer, previous examples must be written in the following form.
Class x<t>
{
public static long F (t)
{
Return (long) (object) T; OK; allow conversion
}
}


--------------------------------------------------------------------------------

[1] In this case ">" is interpreted as being greater than the operator.
[2] When you rewrite ToString in a program, it is generally not recommended to add such a similar computational logic because it is not easily controlled and increases the complexity of the debugger.
[3] C's base class or base class of its base class, and so on.



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.