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

276

Part III Creating Components

Implementing Encapsulation by Using Methods

First let’s recap the original motivation for using methods to hide fields.

Consider the following structure that represents a position on a computer screen as a pair of coordinates, x and y. Assume that the range of valid values for the x-coordinate lies between 0 and 1280 and the range of valid values for the y-coordinate lies between 0 and 1024:

struct ScreenPosition

{

public ScreenPosition(int x, int y)

{

this.X = rangeCheckedX(x); this.Y = rangeCheckedY(y);

}

public int X; public int Y;

private static int rangeCheckedX(int x)

{

if (x < 0 || x > 1280)

{

throw new ArgumentOutOfRangeException(“X”);

}

return x;

}

private static int rangeCheckedY(int y)

{

if (y < 0 || y > 1024)

{

throw new ArgumentOutOfRangeException(“Y”);

}

return y;

}

}

One problem with this structure is that it does not follow the golden rule of encapsulation—

that is, it does not keep its data private. Public data is a bad idea because its use cannot be checked and controlled. For example, the ScreenPosition constructor range checks its param-

eters, but no such check can be done on the “raw” access to the public fields. Sooner or later (probably sooner), either X or Y will stray out of its acceptable range, possibly as the result of a programming error:

ScreenPosition origin = new ScreenPosition(0, 0);

...

int xpos = origin.X; origin.Y = -100; // oops

Chapter 15 Implementing Properties to Access Fields

277

The common way to solve this problem is to make the fields private and add an accessor method and a modifier method to respectively read and write the value of each private field.

The modifier methods can then range-check new field values. For example, the following code contains an accessor (GetX) and a modifier (SetX) for the X field. Notice that SetX checks

its parameter value.

struct ScreenPosition

{

...

public int GetX()

{

return this.x;

}

public void SetX(int newX)

{

this.x = rangeCheckedX(newX);

}

...

private static int rangeCheckedX(int x) { ... } private static int rangeCheckedY(int y) { ... } private int x, y;

}

The code now successfully enforces the range constraints, which is good. However, there is a price to pay for this valuable guarantee—ScreenPosition no longer has a natural fieldlike syn-

tax; it uses awkward method-based syntax instead. The following example increases the value of X by 10. To do so, it has to read the value of X by using the GetX accessor method and

then write the value of X by using the SetX modifier method.

int xpos = origin.GetX(); origin.SetX(xpos + 10);

Compare this with the equivalent code if the X field were public:

origin.X += 10;

There is no doubt that, in this case, using public fields is cleaner, shorter, and easier. Unfortunately, using public fields breaks encapsulation. Properties enable you to combine the best of both examples: to retain encapsulation while allowing a fieldlike syntax.

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