Лекция 14. Обобщенные типы

C выходом версии 2.0 фреймворк.NET стал поддерживать обобщенные типы (generics), а также создание обобщенных методов и делегатов. Чтобы разобраться в особенности данного нововведения, сначала посмотрим на проблему, которая могла возникнуть до появления обобщенных типов. Посмотрим на примере:

int x = 44;

 

System.Collections.ArrayList array = new System.Collections.ArrayList();

// Упаковка значения x в тип Object

array.Add(x);

 

// Распаковка в значение типа int первого элемента коллекции

int y = (int)array[0];

 

Console.WriteLine(y);

Здесь используется класс ArrayList, который представляет коллекцию объектов. Чтобы поместить объект в коллекцию, применяется метод Add. И хотя мы добавляем в коллекцию числа и строки, но по существу ArrayList содержит коллекцию значений типа Object, а это значит, что в вызове array.Add(x) значение переменной x сначала будет "упаковано" в значение типа Object, а потом при получении элементов из коллекции - наоборот, "распаковано" в нужный тип.

Упаковка (boxing) предполагает преобразование объекта значимого типа (например, типа int) к типу object. При упаковке общеязыковая среда CLR обертывает значение в объект типа System.Object и сохраняет его в управляемой куче (хипе). Распаковка (unboxing), наоборот, предполагает преобразование объекта типа object к значимому типу. Упаковка и распаковка ведут к снижению производительности, так как системе надо осуществить необходимые преобразования.

Кроме того, существует другая проблема - проблема безопасности типов. Так, мы получим ошибку во время выполнения программы, если напишем следующим образом:

int x = 44;

 

System.Collections.ArrayList array = new System.Collections.ArrayList();

// Упаковка значения x в тип Object

array.Add(x);

string s = "hello";

// Добавление s в коллекцию ArrayList

array.Add(s);

// по индексу 1 в коллекции array строка s

int y = (int)array[1];

Следом за числом x в коллекцию array добавляется строка s. И попытка получить число по индексу 1, по которому хранится строка "hello", вызовет ошибку.

Эти проблемы были призваны устранить обобщенные типы. Обобщенные типы позволяют указать конкретный тип, который будет использоваться. Например, используем обобщенный вариант класса ArrayList - класс List:

int x = 44;

string s = "hello";

 

List<int> array = new List<int>();

           

array.Add(x);

// Распаковка уже не нужна

int y = array[0];

 

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

array.Add(s);

Поскольку класс List является обобщенным, то нам нужно задать в выражении <тип> тип данных, для которого этот класс будет применяться. Далее мы добавляем число и строку в коллекцию. Но если число будет добавлено в коллекцию array, так как коллекция типизирована типом int, то на строке array.Add(s); мы получим ошибку во время компиляции и должны будем удалить эту строку. Таким образом, используя обобщенный вариант класса, мы снижаем время на выполнение и количество потенциальных ошибок.

Создадим теперь свой обобщенный класс Bank:

class Bank<T>

{

T[] clients;

 

public Bank()

{

}

public Bank(T[] _clients)

{

   this.clients = _clients;

}

}

Используя букву T в описании class Bank<T>, мы указываем, что данный тип будет использоваться этим классом. В классе мы создаем массив объектов этого типа. Причем сейчас нам неизвестно, что это будет за тип. И это может быть любой тип. А параметр T в угловых скобках называется универсальным параметром, так как вместо него можно подставить любой тип.

Например, вместо параметра T можно использовать объект int, то есть число, представляющее номер счета. Это также может быть объект string, либо или любой другой класс или структура:

Bank<int> bank = new Bank<int>(new int[] { 1, 2, 4, 5, 6 });

Bank<string> bank2 = new Bank<string>(new string[] {"13433", "342454", "21432"});

Значения по умолчанию

Иногда возникает необходимость присвоить переменным универсальных параметров некоторое начальное значение, в том числе и null. Но напрямую мы его присвоить не можем:

T id = null;

В этом случае нам надо использовать оператор default(T). Он присваивает ссылочным типам в качестве значения null, а типам значений - значение 0:

class Bank<T>

{

T id = default(T);

}

Статические поля обобщенных классов

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

class Bank<T>

{

public static T session;

           public Bank()

{

}

}

Теперь типизируем класс двумя типами int и string:

Bank<string> bank1 = new Bank<string>();

Bank<string>.session = "45245";

 

Bank<int> bank2 = new Bank<int>();

Bank<int>.session = 5452;

 

Console.WriteLine(Bank<string>.session); // "45245"

В итоге для Bank<string> и для Bank<int> будет создана своя переменная session.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: