public int FindIndex(System.Predicate<Person> match);
public Person FindLast(System.Predicate<Person> match);
public bool Remove(Person item);
public int RemoveAll(System.Predicate<Person> match);
public Person[] ToArray;
public bool TrueForAll(System.Predicate<Person> match);
public Person this[int index] { get; set; }
}
}
Несомненно,
когда вы создаете в коде переменную обобщенного типа
List<T>
, компилятор вовсе не создает новую реализацию класса
List<T>
. Взамен он принимает во внимание только члены обобщенного типа, к которым вы действительно обращаетесь.
Указание параметров типа для обобщенных членов
В необобщенном классе или структуре разрешено поддерживать обобщенные свойства. В таких случаях необходимо также указывать значение заполнителя во время вызова метода. Например, класс
System.Array
поддерживает набор обобщенных методов. В частности, необобщенный статический метод
Sort
имеет обобщенный аналог по имени
Sort<T>
. Рассмотрим представленный далее фрагмент кода, где
Т
— тип
int
:
int[] myInts = { 10, 4, 2, 33, 93 };
// Указание заполнителя для обобщенного метода Sort<>.
Array.Sort<int>(myInts);
foreach (int i in myInts)
{
Console.WriteLine(i);
}
Указание параметров типов для обобщенных интерфейсов
Обобщенные интерфейсы обычно реализуются при построении классов или структур,которые нуждаются в поддержке разнообразных аспектов поведения платформы (скажем, клонирования, сортировки и перечисления). В главе 8 вы узнали о нескольких необобщенных интерфейсах, таких как
IComparable
,
IEnumerable
,
IEnumerator
и
IComparer
. Вспомните, что необобщенный интерфейс
IComparable
определен примерно так:
public interface IComparable
{
int CompareTo(object obj);
}
В главе 8 этот интерфейс также был реализован классом
Car
, чтобы сделать возможной сортировку стандартного массива. Однако код требовал нескольких проверок времени выполнения и операций приведения, потому что параметром был общий тип
System.Object
:
public class Car : IComparable
{
...
// Реализация IComparable.
int IComparable.CompareTo(object obj)
{
if (obj is Car temp)
{
return this.CarID.CompareTo(temp.CarID);
}
throw new ArgumentException("Parameter is not a Car!");
//
Параметр не является объектом типа Car!
}
}
Теперь представим, что применяется обобщенный аналог данного интерфейса:
public interface IComparable<T>
{
int CompareTo(T obj);
}
В таком случае код реализации будет значительно яснее:
public class Car : IComparable<Car>
{
...
// Реализация IComparable<T>.
int IComparable<Car>.CompareTo(Car obj)
{
if (this.CarID > obj.CarID)
{
return 1;
}
if (this.CarID < obj.CarID)
{
return -1;
}
return 0;
}
}
Здесь уже не нужно проверять, относится ли входной параметр к типу
Car
, потому что он может быть только
Car
! В случае передачи несовместимого типа данных возникает ошибка на этапе компиляции. Теперь, углубив понимание того, как взаимодействовать с обобщенными элементами, а также усвоив роль параметров типа (т.е. заполнителей), вы готовы к исследованию классов и интерфейсов из пространства имен
System.Collections.Generic
.
Пространство имен System.Collections.Generic
Когда вы строите приложение .NET Core и необходим способ управления данными в памяти, классы из пространства имен
System.Collections.Generic
вероятно удовлетворят всем требованиям. В начале настоящей главы кратко упоминались некоторые основные необобщенные интерфейсы, реализуемые необобщенными классами коллекций. Не должен вызывать удивление тот факт, что в пространстве имен
System.Collections.Generic
для многих из них определены обобщенные замены.
В действительности вы сможете найти некоторое количество обобщенных интерфейсов, которые расширяют свои необобщенные аналоги, что может показаться странным.Тем не менее, за счет этого реализующие их классы будут также поддерживать унаследованную функциональность, которая имеется в их необобщенных родственных версиях. Например, интерфейс
IEnumerable<T>
расширяет
IEnumerable
. В табл. 10.4 описаны основные обобщенные интерфейсы, с которыми вы столкнетесь во время работы с обобщенными классами коллекций.