Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Daniel Solis - Illustrated C# 2010 - 2010.pdf
Скачиваний:
16
Добавлен:
11.06.2015
Размер:
11.23 Mб
Скачать

CHAPTER 19 GENERICS

Constraints on Type Parameters

In the generic stack example, the stack did not do anything with the items it contained other than store them and pop them. It didn’t try to add them, compare them, or do anything else that would require using operations of the items themselves. There’s good reason for that. Since the generic stack doesn’t know the type of the items it will be storing, it can’t know what members these types implement.

All C# objects, however, are ultimately derived from class object, so the one thing the stack can be sure of about the items it’s storing is that they implement the members of class object. These include methods ToString, Equals, and GetType. Other than that, it can’t know what members are available.

As long as your code doesn’t access the objects of the types it handles (or as long as it sticks to the members of type object), your generic class can handle any type. Type parameters that meet this constraint are called unbounded type parameters. If, however, your code tries to use any other members, the compiler will produce an error message.

For example, the following code declares a class called Simple with a method called LessThan that takes two variables of the same generic type. LessThan attempts to return the result of using the less-than operator. But not all classes implement the less-than operator, so you can’t just substitute any class for T. The compiler, therefore, produces an error message.

class Simple<T>

 

{

 

static public bool LessThan(T i1, T i2)

 

{

 

return i1 < i2;

// Error

}

 

...

 

}

 

To make generics more useful, therefore, you need to be able to supply additional information to the compiler about what kinds of types are acceptable as arguments. These additional bits of information are called constraints. Only types that meet the constraints can be substituted for the given type parameter.

478

CHAPTER 19 GENERICS

Where Clauses

Constraints are listed as where clauses.

Each type parameter that has constraints has its own where clause.

If a parameter has multiple constraints, they are listed in the where clause, separated by commas.

The syntax of a where clause is the following:

Type parameter

Constraint list

where TypeParam : constraint, constraint, ...

Keyword

Colon

The important points about where clauses are the following:

They’re listed after the closing angle bracket of the type parameter list.

They’re not separated by commas or any other token.

They can be listed in any order.

The token where is a contextual keyword, so you can use it in other contexts.

For example, the following generic class has three type parameters. T1 is unbounded. For T2, only classes of type Customer, or classes derived from Customer, can be used as type arguments. For T3, only classes that implement interface IComparable can be used as type arguments.

Unbounded

With constraints

 

 

No separators

 

class MyClass < T1, T2, T3 >

 

where T2: Customer

// Constraint for T2

where T3: IComparable

// Constraint for T3

{

 

 

 

...

 

 

No separators

 

}

479

CHAPTER 19 GENERICS

Constraint Types and Order

There are five types of constraints. These are listed in Table 19-2.

Table 19-2. Types of Constraints

Constraint Type

Description

ClassName

Only classes of this type, or classes derived from it, can be used as the type

 

argument.

class

Any reference type, including classes, arrays, delegates, and interfaces, can be used

 

as the type argument.

struct

Any value type can be used as the type argument.

InterfaceName

Only this interface, or types that implement this interface, can be used as the

 

type argument.

new()

Any type with a parameterless public constructor can be used as the type

 

argument. This is called the constructor constraint.

 

 

The where clauses can be listed in any order. The constraints in a where clause, however, must be placed in a particular order, as shown in Figure 19-9.

There can be at most one primary constraint, and if there is one, it must be listed first.

There can be any number of InterfaceName constraints.

If the constructor constraint is present, it must be listed last.

Figure 19-9. If a type parameter has multiple constraints, they must be in this order.

The following declarations show examples of where clauses:

class SortedList<S>

where S: IComparable<S> { ... }

class LinkedList<M,N>

 

where

M

: IComparable<M>

 

where

N

: ICloneable

{ ... }

class MyDictionary<KeyType, ValueType>

where

KeyType : IEnumerable,

new()

{ ... }

480

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]