Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C# 2008 Step by Step.pdf
Скачиваний:
16
Добавлен:
25.03.2016
Размер:
13.96 Mб
Скачать

Chapter 18

Introducing Generics

After completing this chapter, you will be able to:

Define a type-safe class by using generics.

Create instances of a generic class based on types specified as type parameters.

Implement a generic interface.

Define a generic method that implements an algorithm independent of the type of data on which it operates.

In Chapter 8, “Understanding Values and References,” you learned how to use the object type to refer to an instance of any class. You can use the object type to store a value of any type, and you can define parameters by using the object type when you need to pass values of any type into a method. A method can also return values of any type by specifying object as

the return type. Although this practice is very flexible, it puts the onus on the programmer to remember what sort of data is actually being used and can lead to run-time errors if the programmer makes a mistake. In this chapter, you will learn about generics, a feature that has been designed to help you prevent that kind of mistake.

The Problem with objects

To understand generics, it is worth looking in detail at the problems they are designed to solve, specifically when using the object type.

You can use the object type to refer to a value or variable of any type. All reference types automatically inherit (either directly or indirectly) from the System.Object class in the

Microsoft .NET Framework. You can use this information to create highly generalized classes and methods. For example, many of the classes in the System.Collections namespace exploit

this fact, so you can create collections holding almost any type of data. (You have already been introduced to the collection classes in Chapter 10, “Using Arrays and Collections.”) By homing in on one particular collection class as a detailed example, you will also notice in the

333

334 Part III Creating Components

System.Collections.Queue class that you can create queues containing practically anything. The following code example shows how to create and manipulate a queue of Circle objects:

using System.Collections;

...

Queue myQueue = new Queue(); Circle myCircle = new Circle(); myQueue.Enqueue(myCircle);

...

myCircle = (Circle)myQueue.Dequeue();

The Enqueue method adds an object to the head of a queue, and the Dequeue method removes the object at the other end of the queue. These methods are defined like this:

public void Enqueue( object item ); public object Dequeue();

Because the Enqueue and Dequeue methods manipulate objects, you can operate on queues of Circles, PhoneBooks, Clocks, or any of the other classes you have seen in earlier exercises in

this book. However, it is important to notice that you have to cast the value returned by the Dequeue method to the appropriate type because the compiler will not perform the conversion from the object type automatically. If you don’t cast the returned value, you will get the

compiler error “Cannot implicitly convert type ‘object’ to ‘Circle’. ”

This need to perform an explicit cast denigrates much of the flexibility afforded by the object type. It is very easy to write code such as this:

Queue myQueue = new Queue(); Circle myCircle = new Circle(); myQueue.Enqueue(myCircle);

...

Clock myClock = (Clock)myQueue.Dequeue(); // run-time error

Although this code will compile, it is not valid and throws a System.InvalidCastException at run time. The error is caused by trying to store a reference to a Circle in a Clock variable, and

the two types are not compatible. This error is not spotted until run time because the compiler does not have enough information to perform this check at compile time. The real type of the object being dequeued becomes apparent only when the code runs.

Another disadvantage of using the object approach to create generalized classes and meth-

ods is that it can use additional memory and processor time if the runtime needs to convert an object to a value type and back again. Consider the following piece of code that manipu-

lates a queue of int variables:

Queue myQueue = new Queue();

 

int myInt = 99;

// box the int to an object

myQueue.Enqueue(myInt);

...

myInt = (int)myQueue.Dequeue(); // unbox the object to an int

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