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

Chapter 16 Using Indexers

299

Note You should perform a range check on the index value in the indexer to prevent any unexpected exceptions from occurring in your indexer code.

After you have declared the indexer, you can use a variable of type IntBits instead of an int and apply the square bracket notation, as shown in the next example:

int adapted = 62;

// 62 has the binary representation 111110

IntBits bits = new IntBits(adapted);

 

bool peek = bits[6];

// retrieve bool at index 6; should be true (1)

bits[0] = true;

//

set the bit at index 0

to true (1)

bits[3] = false;

//

set the bit at index 3

to false (0)

// the value in adapted is now 111011, or 59 in decimal

This syntax is certainly much easier to understand. It directly and succinctly captures the essence of the problem.

Note Indexers and properties are similar in that both use get and set accessors. An indexer is like a property with multiple values. However, although you’re allowed to declare static properties, static indexers are illegal.

Understanding Indexer Accessors

When you read an indexer, the compiler automatically translates your arraylike code into a call to the get accessor of that indexer. Consider the following example:

bool peek = bits[6];

This statement is converted to a call to the get accessor for bits, and the index argument is set to 6.

Similarly, if you write to an indexer, the compiler automatically translates your arraylike code into a call to the set accessor of that indexer, setting the index argument to the value en-

closed in the square brackets. For example:

bits[6] = true;

This statement is converted to a call to the set accessor for bits where index is 6. As with

ordinary properties, the data you are writing to the indexer (in this case, true) is made available inside the set accessor by using the value keyword. The type of value is the same as the

type of indexer itself (in this case, bool).

300 Part III Creating Components

It’s also possible to use an indexer in a combined read/write context. In this case, the get and set accessors are both used. Look at the following statement:

bits[6] ^= true;

This code is automatically translated into:

bits[6] = bits[6] ^ true;

This code works because the indexer declares both a get and a set accessor.

Note You can declare an indexer that contains only a get accessor (a read-only indexer) or only a set accessor (a write-only accessor).

Comparing Indexers and Arrays

When you use an indexer, the syntax is deliberately very arraylike. However, there are some important differences between indexers and arrays:

Indexers can use non-numeric subscripts, whereas arrays can use only integer subscripts:

public int this [ string name ] { ... } // OK

Tip Many collection classes, such as Hashtable, that implement an associative lookup

based on key/value pairs implement indexers to provide a convenient alternative to using the Add method to add a new value and as an alternative to iterating through the Values

property to locate a value in your code. For example, instead of this:

Hashtable ages = new Hashtable(); ages.Add(“John”, 42);

you can use this:

Hashtable ages = new Hashtable(); ages[“John”] = 42;

Indexers can be overloaded (just like methods), whereas arrays cannot:

public

Name

this

[

PhoneNumber

number ] { ...

}

public

PhoneNumber

this

[

Name name ]

{ ...

}

 

Indexers cannot be used as ref or out parameters, whereas array elements can:

IntBits bits;

//

bits contains an indexer

Method(ref bits[1]);

//

compile-time error

Chapter 16 Using Indexers

301

Properties, Arrays, and Indexers

It is possible for a property to return an array, but remember that arrays are reference types, so exposing an array as a property makes it possible to accidentally overwrite a lot of data. Look at the following structure that exposes an array property named Data:

struct Wrapper

{

private int[] data;

...

public int[] Data

{

get { return this.data; } set { this.data = value; }

}

}

Now consider the following code that uses this property:

Wrapper wrap = new Wrapper();

...

int[] myData = wrap.Data; myData[0]++;

myData[1]++;

This looks pretty innocuous. However, because arrays are reference types, the variable myData refers to the same object as the private data variable in the Wrapper structure. Any changes you make to elements in myData are made to the data array; the expression myData[0]++ has exactly the same effect as data[0]++. If this is not the intention, you should use the Clone method in the get and set accessors of the Data property to

return a copy of the data array, or make a copy of the value being set, as shown here. (The Clone method returns an object, which you must cast to an integer array.)

struct Wrapper

{

private int[] data;

...

public int[] Data

{

get { return this.data.Clone() as int[]; } set { this.data = value.Clone() as int[]; }

}

}

However, this approach can become very messy and expensive in terms of memory use. Indexers provide a natural solution to this problem—don’t expose the entire array as a property; just make its individual elements available through an indexer:

struct Wrapper

{

private int[] data;

...

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