public virtual void Remove(<b>object?</b> obj);
public virtual <b>object?</b> this[int index] {get; set; }
}
Класс ArrayList был построен для оперирования с экземплярами object, которые представляют данные, находящиеся в куче, поэтому может показаться странным, что следующий код компилируется и выполняется без ошибок:
static void WorkWithArrayList()
{
// Типы значений автоматически упаковываются при передаче
// методу, который требует экземпляр типа object.
ArrayList myInts = new ArrayList();
myInts.Add(10);
myInts.Add(20);
myInts.Add(35);
}
Хотя здесь числовые данные напрямую передаются методам, которые требуют экземпляров типа object, исполняющая среда выполняет автоматическую упаковку таких основанных на стеке данных. Когда позже понадобится извлечь элемент из ArrayList с применением индексатора типа, находящийся в куче объект должен быть распакован в целочисленное значение, расположенное в стеке, посредством операции приведения. Не забывайте, что индексатор ArrayList возвращает элементы типа System.Object, а не System.Int32:
static void WorkWithArrayList()
{
<b> // Типы значений автоматически упаковываются,</b>
<b> // когда передаются члену, принимающему object.</b>
ArrayList myInts = new ArrayList();
myInts.Add(10);
myInts.Add(20);
myInts.Add(35);
<b> // Распаковка происходит, когда объект преобразуется</b>
<b> // обратно в данные, расположенные в стеке.</b>
int i = (int)myInts[0];
<b> // Теперь значение вновь упаковывается, т.к.</b>
<b> // метод WriteLine() требует типа object!</b>
Console.WriteLine("Value of your int: {0}", i);
}
Обратите внимание, что расположенное в стеке значение типа System.Int32 перед вызовом метода ArrayList.Add() упаковывается, чтобы оно могло быть передано в требуемом виде System.Object. Вдобавок объект System.Object распаковывается обратно в System.Int32 после его извлечения из ArrayList через операцию приведения лишь для того, чтобы снова быть упакованными при передаче методу Console.WriteLine(), поскольку данный метод работает с типом System.Object.
(window.adrunTag = window.adrunTag || []).push({v: 1, el: 'adrun-4-390', c: 4, b: 390})
Упаковка и распаковка удобны с точки зрения программиста, но такой упрощенный подход к передаче данных между стеком и кучей влечет за собой проблемы, связанные с производительностью (снижение скорости выполнения и увеличение размера кода), а также приводит к утрате безопасности в отношении типов. Чтобы понять проблемы с производительностью, примите во внимание действия, которые должны произойти при упаковке и распаковке простого целочисленного значения.
1. Новый объект должен быть размещен в управляемой куче.
2. Значение данных, находящееся в стеке, должно быть передано в выделенное место в памяти.
3. При распаковке значение, которое хранится в объекте, находящемся в куче, должно быть передано обратно в стек.
4. Неиспользуемый в дальнейшем объект, расположенный в куче, будет (со временем) удален сборщиком мусора.
Несмотря на то что показанный конкретный метод WorkWithArrayList() не создает значительное узкое место в плане производительности, вы определенно заметите такое влияние, если ArrayList будет содержать тысячи целочисленных значений, которыми программа манипулирует на регулярной основе. В идеальном мире мы могли бы обрабатывать данные, находящиеся внутри контейнера в стеке, безо всяких проблем с производительностью. Было бы замечательно иметь возможность извлекать данные из контейнера, не прибегая к конструкциям try/catch (именно это позволяют делать обобщения).
Проблема безопасности в отношении типов
Мы уже затрагивали проблему безопасности в отношении типов, когда рассматривали операции распаковки. Вспомните, что данные должны быть распакованы в тот же самый тип, с которым они объявлялись перед упаковкой. Однако существует еще один аспект безопасности в отношении типов, который необходимо иметь в виду в мире без обобщений: тот факт, что классы из пространства имен System.Collections обычно могут хранить любые данные, т.к. их члены прототипированы для оперирования с типом System.Object. Например, следующий метод строит список ArrayList с произвольными фрагментами несвязанных данных:
static void ArrayListOfRandomObjects()
{
<b> // ArrayList может хранить вообще все что угодно.</b>
ArrayList allMyObjects = new ArrayList();
allMyObjects.Add(true);
allMyObjects.Add(new OperatingSystem(PlatformID.MacOSX,
new Version(10, 0)));
allMyObjects.Add(66);
allMyObjects.Add(3.14);
}
В ряде случаев вам будет требоваться исключительно гибкий контейнер, который способен хранить буквально все (как было здесь показано). Но большую часть времени вас интересует безопасный в отношении типов контейнер, который может работать только с определенным типом данных. Например, вы можете нуждаться в контейнере, хранящем только объекты типа подключения к базе данных, растрового изображения или класса, реализующего интерфейс IPointy.