Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C-sharp language specification.2004.pdf
Скачиваний:
14
Добавлен:
23.08.2013
Размер:
2.55 Mб
Скачать

Chapter 26 Generics

1If T has both interface-type constraints and type-parameter constraints, its effective interface set is the

2union of its set of interface-type constraints and the effective interface sets of its type-parameter

3constraints.

4A type parameter is known to be a reference type if it has the reference type constraint or its effective base

5class is not object or System.ValueType.

6Values of a constrained type parameter type can be used to access the instance members implied by the

7constraints. [Example: In the following code

8interface IPrintable

9{

10void Print();

11}

12class Printer<T> where T: IPrintable

13{

14

void PrintOne(T x) {

15

x.Print();

16}

17}

18the methods of IPrintable can be invoked directly on x because T is constrained to always implement

19IPrintable. end example]

20Two generic partial type declarations (in the same program) contribute to the same unbound generic type if

21they have the same fully qualified name (which includes the number of type parameters) (§10.3). Two such

22partial type declarations shall specify the same name for each type parameter, in order.

23When a partial generic type declaration includes constraints, the constraints shall agree with all other parts

24that include constraints. Specifically, each part that includes constraints shall have constraints for the same

25set of type parameters, and for each type parameter, the sets of primary, secondary, and constructor

26constraints shall be equivalent. Two sets of constraints are equivalent if they contain the same members. If

27no part of a partial generic type specifies type parameter constraints, the type parameters are considered

28unconstrained. [Example:

29partial class Map<K,V>

30

where K: IComparable<K>

31where V: IKeyProvider<K>, new()

32{

33...

34}

35partial class Map<K,V>

36

where V: IKeyProvider<K>, new()

37where K: IComparable<K>

38{

39...

40}

41partial class Map<K,V>

42{

43...

44}

45is correct because those parts that include constraints (the first two) effectively specify the same set of

46primary, secondary, and constructor constraints for the same set of type parameters, respectively. end

47example]

4826.7.1 Satisfying constraints

49Whenever a constructed type or generic method is referenced, the supplied type arguments are checked

50against the type parameter constraints declared on the generic type or method. For each where clause, the

51type argument A that corresponds to the named type parameter is checked against each constraint as follows:

419

C# LANGUAGE SPECIFICATION

1If the constraint is a class type, an interface type, or a type parameter, let C represent that constraint with

2the supplied type arguments substituted for any type parameters that appear in the constraint. To satisfy

3the constraint, it shall be the case that type A is convertible to type C by one of the following:

4o An identity conversion (§13.1.1)

5o An implicit reference conversion (§13.1.4)

6o A boxing conversion (§13.1.5)

7o An implicit conversion from a type parameter A to C (§26.7.4).

8If the constraint is the reference type constraint, the type A must satisfy one of the following:

9o A is an interface type, class type, delegate type or array type. [Note: Note that System.ValueType

10and System.Enum are reference types so satisfy this constraint. end note]

11o A is a type parameter that is known to be a reference type (§26.7).

12If the constraint is the value type constraint, the type A must satisfy one of the following:

13o A is a struct type or enum type. [Note: Note that System.ValueType and System.Enum are

14reference types so do not satisfy this constraint. end note]

15o A is a type parameter having the value type constraint (§26.7).

16If the constraint is the constructor constraint new(), the type argument A shall not be abstract and

17shall have a public parameterless constructor. This is satisfied if one of the following is true:

18o A is a value type, since all value types have a public default constructor (§11.1.2).

19o A is a type parameter having the value type constraint (§26.7).

20o A is a class that is not abstract, A contains an explicitly declared public constructor with no

21parameters.

22o A is not abstract and has a default constructor (§17.10.4).

23A compile-time error occurs if one or more of a type parameter’s constraints are not satisfied by the given

24type arguments.

25Since type parameters are not inherited, constraints are never inherited either. [Example: In the code below,

26D needs to specify the constraint on its type parameter T, so that T satisfies the constraint imposed by the

27base class B<T>. In contrast, class E need not specify a constraint, because List<T> implements

28IEnumerable for any T.

29class B<T> where T: IEnumerable {…}

30class D<T>: B<T> where T: IEnumerable {…}

31class E<T>: B<List<T>> {…}

32end example]

3326.7.2 Member lookup on type parameters

34The results of member lookup in a type given by a type parameter T depends on the constraints, if any,

35specified for T. If T has no class-type, interface-type or type-parameter constraints, then member lookup on

36T returns the same set of members as member lookup on object. Otherwise, the first stage of member

37lookup (§14.3) considers all the members in the effective base class of T and all the members in each

38interface in the effective interface set of T. After performing the first stage of member lookup for each of

39theses types, the results are combined, and then hidden members are removed from the combined results.

40Before the advent of generics, member lookup always returned either a set of members declared solely in

41classes, or a set of members declared solely in interfaces and possibly the type object. Member lookup on

42type parameters changes this somewhat. When a type parameter has both an effective base class other than

43object and a non-empty effective interface set, member lookup can return a set of members, some of which

420

Chapter 26 Generics

1were declared in a class, and others of which were declared in an interface. The following additional rules

2handle this case.

3As specified in §14.3, during member lookup, members declared in a class other than object hide

4members declared in interfaces.

5During overload resolution of methods (§14.5.5.1) and indexers (§14.5.6.2), if any applicable member

6was declared in a class other than object, all members declared in an interface are removed from the

7set of considered members.

8These rules only have effect when doing binding on a type parameter with both an effective base class other

9than object and a non-empty effective interface set. [Note: Informally, members defined in a class

10constraint are preferred over members in an interface constraint. end note]

1126.7.3 Type parameters and boxing

12When a struct type overrides a virtual method inherited from System.Object (such as Equals,

13GetHashCode, or ToString), invocation of the virtual method through an instance of the struct type

14doesn’t cause boxing to occur. This is true even when the struct is used as a type parameter and the

15invocation occurs through an instance of the type parameter type. [Example:

16using System;

17struct Counter

18{

19

int value;

20

public override string ToString() {

21

value++;

22

return value.ToString();

23}

24}

25class Program

26{

27

static void Test<T>() where T: new() {

28

T x = new T();

29

Console.WriteLine(x.ToString());

30

Console.WriteLine(x.ToString());

31

Console.WriteLine(x.ToString());

32

}

33

static void Main() {

34

Test<Counter>();

35}

36}

37The output of the program is:

381

392

403

41Although it is bad style for ToString to have side effects, the example demonstrates that no boxing

42occurred for the three invocations of x.ToString(). end example]

43Similarly, boxing never implicitly occurs when accessing a member on a constrained type parameter.

44[Example: Suppose an interface ICounter contains a method Increment which can be used to modify a

45value. If ICounter is used as a constraint, the implementation of the Increment method is called with a

46reference to the variable that Increment was called on, never a boxed copy. This behaviour is different

47than the non-generic case. When making a call to an interface implementation on a struct type the argument

48is always boxed.

49using System;

50interface ICounter

51{

52void Increment();

53}

421

C# LANGUAGE SPECIFICATION

1struct Counter: ICounter

2{

3

int value;

4

public override string ToString() {

5

return value.ToString();

6

}

7

void ICounter.Increment() {

8

value++;

9}

10}

11class Program

12{

13

static void Test<T>() where T: ICounter, new() {

14

T x = new T();

 

15

Console.WriteLine(x);

 

16

x.Increment();

// Modify x

17

Console.WriteLine(x);

 

18

((ICounter)x).Increment();

// Modify boxed copy of x

19

Console.WriteLine(x);

 

20

}

 

21

static void Main() {

 

22

Test<Counter>();

 

23}

24}

25The first call to Increment modifies the value in the variable x. This is not equivalent to the second call to

26Increment, which modifies the value in a boxed copy of x. Thus, the output of the program is:

270

281

291

30end example]

3126.7.4 Conversions involving type parameters

32The conversions that are allowed on a type parameter T depend on the constraints specified for T and are

33detailed in §13.

34The conversion rules do not permit a direct explicit conversion from an unconstrained type parameter to an

35arbitrary non-interface type, which might be surprising. The reason for this rule is to prevent confusion and

36make the semantics of such conversions clear. [Example: Consider the following declaration:

37class X<T>

38{

39

public static long

F(T t) {

40

return (long)t;

// Error, explicit conversion not permitted

41}

42}

43If the direct explicit conversion of t to long were permitted, one might easily expect that X<int>.F(7)

44would return 7L. However, it would not, because the standard numeric conversions are only considered

45when the types are known to be numeric at compile time. In order to make the semantics clear, the above

46example should be written:

47class X<T>

48{

49

public static long F(T t) {

 

50

return (long)(object)t;

// OK, conversions permitted

51}

52}

53This code will now compile but executing X<int>.F(7) would then throw an exception at runtime, since a

54boxed int cannot be converted directly to a long.

55end example]

422

Соседние файлы в предмете Электротехника