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 и ArrayList):
public 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 следующий оператор using:
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})
using System.Collections.Generic;
Взгляните на показанный ниже метод (добавленный в конец файла Program.cs), в котором используется класс List<T> (из пространства имен System.Collection.Generic) для хранения разнообразных видов данных в строго типизированной манере (пока не обращайте внимания на детали синтаксиса обобщений):
static 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. Ниже приведен краткий перечень преимуществ обобщенных контейнеров по сравнению с их необобщенными аналогами.
• Обобщения обеспечивают лучшую производительность, т.к. лишены накладных расходов по упаковке/распаковке, когда хранят типы значений.
• Обобщения безопасны в отношении типов, потому что могут содержать только объекты указанного типа.
• Обобщения значительно сокращают потребность в специальных типах коллекций, поскольку при создании обобщенного контейнера указывается "вид типа".