Язык программирования C#9 и платформа .NET5
using System.Collections;public class CarCollection : IEnumerable{private ArrayList arCars = new ArrayList();// Приведение для вызывающего кода.public Car GetCar(int pos) => (Car) arCars[pos];// Вставка только объектов Car.public void AddCar(Car c){arCars.Add(c);}public void ClearCars(){arCars.Clear();}public int Count => arCars.Count;// Поддержка перечисления с помощью foreach.IEnumerator IEnumerable.GetEnumerator() => arCars.GetEnumerator();}Тем не менее, класс специальной коллекции ничего не делает для решения проблемы с накладными расходами по упаковке/распаковке. Даже если создать специальную коллекцию по имени
, которая предназначена для работы только с элементамиIntCollection, то все равно придется выделять память под объект какого-нибудь вида, хранящий данные (например,System.Int32иSystem.Array):ArrayListpublic class IntCollection : IEnumerable{private ArrayList arInts = new ArrayList();<b> // Получение int (выполняется распаковка).</b>public int GetInt(int pos) => (int)arInts[pos];<b> // Вставка int (выполняется упаковка).</b>public void AddInt(int i){arInts.Add(i);}public void ClearInts(){arInts.Clear();}public int Count => arInts.Count;IEnumerator IEnumerable.GetEnumerator() => arInts.GetEnumerator();}Независимо от того, какой тип выбран для хранения целых чисел, в случае применения необобщенных контейнеров затруднительного положения с упаковкой избежать невозможно.
Первый взгляд на обобщенные коллекции
Когда используются классы обобщенных коллекций, все описанные выше проблемы исчезают, включая накладные расходы на упаковку/распаковку и отсутствие безопасности в отношении типов. К тому же необходимость в создании специального класса (обобщенной) коллекции становится довольно редкой. Вместо построения уникальных классов, которые могут хранить объекты людей, автомобилей и целые числа, можно задействовать класс обобщенной коллекции и указать тип хранимых элементов. Добавьте в начало файла
следующий операторProgram.cs:usingusing System.Collections.Generic;Взгляните на показанный ниже метод (добавленный в конец файла
), в котором используется классProgram.cs(из пространства именList<T>) для хранения разнообразных видов данных в строго типизированной манере (пока не обращайте внимания на детали синтаксиса обобщений):System.Collection.Genericstatic void UseGenericList(){Console.WriteLine("***** Fun with Generics *****\n");<b> // Этот объект List<> может хранить только объекты Person.</b>List<Person> morePeople = new List<Person>();morePeople.Add(new Person ("Frank", "Black", 50));Console.WriteLine(morePeople[0]);<b> // Этот объект ListO может хранить только целые числа.</b>List<int> moreInts = new List<int>();moreInts.Add(10);moreInts.Add(2);int sum = moreInts[0] + moreInts[1];<b> // Ошибка на этапе компиляции! Объект Person</b><b> // не может быть добавлен в список элементов int!</b>// moreInts.Add(new Person());}Первый контейнер
способен содержать только объектыList<T>. По этой причине выполнять приведение при извлечении элементов из контейнера не требуется, что делает такой подход более безопасным в отношении типов. Второй контейнерPersonможет хранить только целые числа, размещенные в стеке; другими словами, здесь не происходит никакой скрытой упаковки/распаковки, которая имеет место в необобщенном типеList<T>. Ниже приведен краткий перечень преимуществ обобщенных контейнеров по сравнению с их необобщенными аналогами.ArrayList• Обобщения обеспечивают лучшую производительность, т.к. лишены накладных расходов по упаковке/распаковке, когда хранят типы значений.
• Обобщения безопасны в отношении типов, потому что могут содержать только объекты указанного типа.
• Обобщения значительно сокращают потребность в специальных типах коллекций, поскольку при создании обобщенного контейнера указывается "вид типа".