Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Программирование / Конспект лекций (C#).pdf
Скачиваний:
864
Добавлен:
27.05.2015
Размер:
2.03 Mб
Скачать

sc.Set("Пример");

string s = sc.Get(); // s = "Пример"

ic = sc;

//

Ошибка

при

компиляции

i = sc.Get();

//

Ошибка

при

компиляции

Так как универсальные классы поддерживают контроль типов параметров, то это позволяет создавать более защищённые программы (по сравнению с программами, использующими в качестве параметра тип object).

Универсальный класс может иметь более одного параметра типа. В этом случае, при описании класса она перечисляются через запятую.

Дополним приведённый выше класс методом, «обнуляющим» значение поля Value. При реализации такого метода возникает вопрос: какое значение присвоить в качестве «нулевого», ведь для разных типов это может быть 0, null, "" и др. Для решения этой проблемы служит оператор default(<тип>):

class MyClass<T>

{

T Value;

public T Get() { return Value; }

public void Set(T NewValue) { Value = NewValue; } public void Clear() { Value = default(T); }

}

7.2 Ограничения по параметрам типа

В простейшем случае, в качестве аргументов типа может выступать любой тип данных. Однако возможна ситуация, когда требуется ввести ограничения на список типов, которые могут быть использованы в качестве аргументов типа.

Общая форма описания класса с ограничениями на параметры типа имеет вид:

class <имя класса><<список параметров типа>>

where <параметр 1> : <ограничение 1_1>[,<ограничение 1_2>...] [where <параметр 2> : <ограничение 2_1>[,<ограничение 2_2>...]...]

{

<члены класса>

}

На параметры могут быть наложения ограничения следующих типов:

ограничение на базовый класс, требующее наличия определённого базового класса в аргументе типа. Это ограничение накладывается указанием имени требуемого базового класса;

ограничение на интерфейс, требующее реализации одного или нескольких интерфейсов аргументом типа. Это ограничение накладывается указанием имени требуемого интерфейса;

ограничение на конструктор, требующее предоставить конструктор без параметров в аргументе типа. Это ограничение накладывается с помощью оператора new();

102

ограничение ссылочного типа, требующее указывать аргумент ссылочного типа с помощью оператора class;

ограничение типа значения, требующее указывать аргумент типа значения с по-

мощью оператора struct.

При наличии на один параметр нескольких ограничений первым должно быть указано ограничение class либо struct, если оно присутствует, или же ограничение на базовый класс, если оно накладывается. Указывать ограничения class или struct одновременно с ограничением на базовый класс не разрешается. Далее по списку должно следовать ограничение на интерфейс, а последним по порядку – ограничение new().

7.2.1 Ограничение на базовый класс

Ограничение на базовый класс позволяет указывать базовый класс, который должен наследоваться аргументом типа. Ограничение на базовый класс служит двум главным целям:

во-первых, оно позволяет использовать в универсальном типе доступные члены того базового класса, на который указывает данное ограничение. В отсутствие ограничения на базовый класс компилятору ничего не известно о типе членов, которые может иметь аргумент типа;

во-вторых, ограничение на базовый класс гарантирует использование только тех аргументов типа, которые поддерживают указанный базовый класс. Это означает, что для любого ограничения, накладываемого на базовый класс, аргумент типа должен обозначать сам базовый класс или производный от него класс. Если же попытаться использовать аргумент типа, не соответствующий указанному базовому классу или не наследующий его, то в результате возникнет ошибка во время компиляции.

Пример: создать универсальный класс, хранящий наследников класса Figure и позволяющий рассчитать их общую площадь.

class MasFigures<T> where T : Square

{

T[] Mas = new T[10]; int Count = 0;

public void Add(T Fig)

{

if (Count < 10) Mas[Count++] = Fig;

}

public T MaxFigure()

{

if (Count > 0)

{

T Max = Mas[0];

for (int i = 1; i < Count; i++) if (Max.size < Mas[i].size)

103

Max = Mas[i]; return Max;

}

else

return null;

}

}

MasFigures<Square> MasSquare = new MasFigures<Square>(); Square Fig = new Square();

Fig.size = 3; MasSquare.Add(Fig); Fig = new Square(); Fig.size = 5; MasSquare.Add(Fig); Fig = new Square(); Fig.size = 4; MasSquare.Add(Fig);

Fig = MasSquare.MaxFigure(); // Вторая фигура, у которой size=5 Rectangle Fig2 = new Rectangle();

MasSquare.Add(Fig2); // Такой действие невозможно

Если в классе MasFigures заменить ограничение на тип Figure, то найти самую большую фигуру через размер будет невозможно, т.к. у класса Figure нет размера. Однако, будет возможно реализовать поиск через площадь, и в этом случае добавление в массив прямоугольника будет вполне допустимым.

7.2.2 Ограничение на интерфейс

Ограничение на интерфейс служит тем же целям, что и ограничение на базовый класс.

Пример: создать универсальный класс, хранящий классы с реализацией интерфейса I2D и позволяющий рассчитать их общий периметр.

class MasI2D<T> where T : I2D

{

T[] Mas = new T[10]; int Count = 0;

public void Add(T Fig)

{

if (Count < 10) Mas[Count++] = Fig;

}

public double SumPerimeter()

{

double Sum = 0;

for (int i = 0; i < Count; i++) Sum += Mas[i].Perimeter();

return Sum;

104

}

}

Square A = new Square(); A.size = 5;

Rectangle B = new Rectangle(); B.size1 = 4;

B.size2 = 7;

Cube C = new Cube(); C.size = 13;

MasI2D<I2D> Mas = new MasI2D<I2D>(); Mas.Add(A);

Mas.Add(B);

Mas.Add(C); // Эта строка не допустима

double SumPerimeter = Mas.SumPerimeter(); // SumPerimeter = 42

7.2.3 Ограничение на конструктор

Ограничение на конструктор необходимо, чтобы в универсальном классе можно было создавать объекты класса «аргумент типа». Если такое ограничение не наложить, то не гарантируется, что в классе «аргумент типа» есть конструктор без параметров. Например:

class MyClass1

{

int A;

public MyClass1() { A = 5; }

public MyClass1(int NewA) { A = NewA; }

}

class MyClass2

{

int A;

public MyClass2(int NewA) { A = NewA; }

}

class MyClass3<T> where T : new()

{

T B;

public MyClass3()

{

//Без ограничения «new()» в строке заголовка класса

//нижеприведенная строка будет выдавать ошибку при компиляции

B = new T();

}

}

MyClass3<MyClass1> cl1 = new MyClass3<MyClass1>(); // Допустимо

//Следующая строка выдаст ошибку компиляции, т.к. в классе

//MyClass2 нет конструктора без параметров

MyClass3<MyClass2> cl2 = new MyClass3<MyClass2>();

105