Чтение онлайн

ЖАНРЫ

Язык программирования C#9 и платформа .NET5
Шрифт:

List<int> moreInts = new List<int>;

moreInts.Add(10);

moreInts.Add(2);

int sum = moreInts[0] + moreInts[1];

// Ошибка на этапе компиляции! Объект Person

// не может быть добавлен в список элементов int!

// moreInts.Add(new Person);

}

Первый контейнер

List<T>
способен содержать только объекты
Person
. По этой причине выполнять приведение
при извлечении элементов из контейнера не требуется, что делает такой подход более безопасным в отношении типов. Второй контейнер
List<T>
может хранить только целые числа, размещенные в стеке; другими словами, здесь не происходит никакой скрытой упаковки/распаковки, которая имеет место в необобщенном типе
ArrayList
. Ниже приведен краткий перечень преимуществ обобщенных контейнеров по сравнению с их необобщенными аналогами.

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

• Обобщения безопасны в отношении типов, потому что могут содержать только объекты указанного типа.

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

Роль параметров обобщенных типов

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

На заметку! Обобщенным образом могут быть записаны только классы, структуры, интерфейсы и делегаты, но не перечисления.

Глядя на обобщенный элемент в документации по .NET Core или в браузере объектов Visual Studio, вы заметите пару угловых скобок с буквой или другой лексемой внутри. На рис. 10.1 показано окно браузера объектов Visual Studio, в котором отображается набор обобщенных элементов из пространства имен

System.Collections.Generic
, включающий выделенный класс
List<T>
.

Формально эти лексемы называются параметрами типа, но в более дружественных к пользователю терминах на них можно ссылаться просто как на заполнители. Конструкцию

<Т>
можно читать как "типа
Т
". Таким образом,
IEnumerable<T>
можно прочитать как "
IEnumerable
типа
Т
".

На заметку! Имя параметра типа (заполнитель) роли не играет и зависит от предпочтений разработчика, создавшего обобщенный элемент. Однако обычно имя

T
применяется для представления типов,
ТКеу
или
К
— для представления ключей и
TValue
или
V
— для представления значений.

Когда вы создаете обобщенный объект, реализуете обобщенный интерфейс или вызываете обобщенный член, на вас возлагается обязанность по предоставлению значения для параметра типа. Многочисленные примеры вы увидите как в этой главе, так и в остальных материалах книги. Тем не менее, для начала рассмотрим основы взаимодействия с обобщенными типами и членами.

Указание параметров типа для обобщенных классов и структур

При создании экземпляра обобщенного класса или структуры вы указываете параметр типа, когда объявляете переменную и когда вызываете конструктор. Как было показано в предыдущем

фрагменте кода, в методе
UseGenericList
определены два объекта
List<T>
:

// Этот объект List<> может хранить только объекты Person.

List<Person> morePeople = new List<Person>;

// Этот объект List<> может хранить только целые числа.

List<int> moreInts = new List<int>;

Первую строку приведенного выше кода можно трактовать как "список

List<>
объектов
Т
, где
Т
— тип
Person
" или более просто как "список объектов действующих лиц". После указания параметра типа обобщенного элемента изменить его нельзя (помните, что сущностью обобщений является безопасность в отношении типов). Когда параметр типа задается для обобщенного класса или структуры, все вхождения заполнителя (заполнителей) заменяются предоставленным значением.

Если вы просмотрите полное объявление обобщенного класса

List<T>
в браузере объектов Visual Studio, то заметите, что заполнитель
Т
используется в определении повсеместно. Ниже приведен частичный листинг:

// Частичное определение класса List<T>.

namespace System.Collections.Generic

{

public class List<T> : IList<T>, IList, IReadOnlyList<T>

{

...

public void Add(T item);

public void AddRange(IEnumerable<T> collection);

public ReadOnlyCollection<T> AsReadOnly;

public int BinarySearch(T item);

public bool Contains(T item);

public void CopyTo(T[] array);

public int FindIndex(System.Predicate<T> match);

public T FindLast(System.Predicate<T> match);

public bool Remove(T item);

public int RemoveAll(System.Predicate<T> match);

public T[] ToArray;

public bool TrueForAll(System.Predicate<T> match);

public T this[int index] { get; set; }

}

}

В случае создания

List<T>
с указанием объектов
Person
результат будет таким же, как если бы тип
List<T>
был определен следующим образом:

namespace System.Collections.Generic

{

public class List<Person>

: IList<Person>, IList, IReadOnlyList<Person>

{

...

public void Add(Person item);

public void AddRange(IEnumerable<Person> collection);

public ReadOnlyCollection<Person> AsReadOnly;

public int BinarySearch(Person item);

Поделиться с друзьями: