Язык программирования C#9 и платформа .NET5 - Троелсен Эндрю
Роль параметров обобщенных типов
Обобщенные классы, интерфейсы, структуры и делегаты вы можете обнаружить повсюду в библиотеках базовых классов .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>
Т
// Частичное определение класса List<T>.
namespace System.Collections.Generic
{
public class List<T> : IList<T>, IList, IReadOnlyList<T>
{
...
public void Add(<b>T</b> item);
public void AddRange(IEnumerable<<b>T</b>> collection);
public ReadOnlyCollection<<b>T</b>> AsReadOnly();
public int BinarySearch(<b>T</b> item);
public bool Contains(<b>T</b> item);
public void CopyTo(<b>T</b>[] array);
public int FindIndex(System.Predicate<<b>T</b>> match);
public T FindLast(System.Predicate<<b>T</b>> match);
public bool Remove(<b>T</b> item);
public int RemoveAll(System.Predicate<<b>T</b>> match);
public <b>T</b>[] ToArray();
public bool TrueForAll(System.Predicate<<b>T</b>> match);
public <b>T</b> this[int index] { get; set; }
}
}
В случае создания
List<T>
Person
List<T>
namespace System.Collections.Generic
{
public class List<<b>Person</b>>
: IList<<b>Person</b>>, IList, IReadOnlyList<<b>Person</b>>
{
...
public void Add(<b>Person</b> item);
public void AddRange(IEnumerable<<b>Person</b>> collection);
public ReadOnlyCollection<<b>Person</b>> AsReadOnly();