static void Main(string[] args) {
// При передаче данных члену, требующему объект, для
// характеризуемых значениями типов автоматически создается
// объектный образ.
ArrayList myInts = new ArrayList();
myInts.Add(10) ;
Console.ReadLine();
}
Чтобы восстановить значение из объекта ArrayList, используя индексатор типа, вы должны превратить размещенный в динамической памяти объект в размещенное в стеке целое число, используя операцию преобразования.
static void Main(string[] args) {
…
// Значение восстанавливается… и снова становится объектом!
Console.WriteLine("Значение вашего int: {0}", (int)myInts[0]);
Console.ReadLine();
}
Для представления операции создания объектного образа в терминах CIL компилятор C# использует блок box. Точно так же операция восстановления из объектного образа преобразуется в CIL-блок unbox. Вот соответствующий CIL-код для показанного выше метода Main() (этот код можно увидеть с помощью ildasm.exe).
.method private hidebysig static void Main(string[] args) cil managed {
…
box [mscorlib]System.Int32
callvirt instance int32 [mscorlib] System.Collections.ArrayList::Add(object)
pop
ldstr "Значение вашего int: {0}"
ldloc.0
ldc.i4.0
callvirt instance object [mscorlib] System.Collections.ArrayList::get_Item(int32)
unbox [mscorlib]System.Int32
ldind.i4
box [mscorlib]System.Int32
call void [mscorlib]System.Console::WriteLine(string, object)
…
}
Обратите внимание на то. что перед обращением к ArrayList.Add() размещенное в стеке значение System.Int32 преобразуется в объект, чтобы передать требуемый System.Object. Также заметьте, что при чтении из ArrayList с помощью индексатора типа (что отображается в скрытый метод get_Item()) объект System.Object восстанавливается в System.Int32 только для того, чтобы снова стать объектным образом при передаче методу Console.WriteLine().
Проблемы создания объектных образов и восстановления значений
Операции создания объектных образов и восстановления из них значений очень удобны с точки зрении программиста, но такой упрощенный подход при обмене элементами стека и динамической памяти имеет свои специфические проблемы производительности и не гарантирует типовой безопасности. Чтобы понять проблемы производительности, рассмотрим следующие шаги, которые приходится выполнять при создании объектного образа и восстановлении значения обычного целого числа.
1. Новый объект нужно разместить в управляемой динамической памяти.
2. Значение размещенных в стеке данных нужно записать в соответствующее место в памяти.
3. При восстановлении значений, сохраненного в объекте, размещенном в динамической памяти, это значение нужно снова вернуть в стек.
4. Неиспользуемый объект в управляемой динамической памяти (в конце концов) должен быть уничтожен сборщиком мусора.
В нашей ситуации метод Main() с точки зрения производительности не имеет никаких проблем, но соответствующие проблемы появятся, когда ArrayList будет содержать тысячи целых значений, если вашей программе придется регулярно их обрабатывать.
Теперь рассмотрим проблему отсутствия: типовой безопасности в отношении операции восстановления значений из объектного образа. Вы знаете, что для восстановления значения в рамках синтаксиса C# используется оператор преобразования. Но каким будет это преобразование – успешным или неудачным, – выяснится только в среде выполнения, При попытке восстановить значение в неправильный тип данных вы получите InvalidCastException.
static void Main(string[] args) {
…
// Ой! Исключение времени выполнения!
Console.WriteLine("Значение вашего int: {0}", (short)myInts[0]);
Console.ReadLine();
}
В идеальной ситуации компилятор C# должен решать проблемы некорректных операций восстановления из объектного образа во время компиляции, а не в среде выполнения. В связи с этим, в действительно идеальной ситуации, можно было бы сохранить типы, характеризуемые значениями, в контейнере, который не требовал бы преобразования в объект. В .NET 2.0 обобщения дают решение именно этих проблем. Однако перед тем как углубиться в детали использования обобщений, давайте посмотрим, как программисты пытались бороться с этими проблемами в .NET 1.x. с помощью строго типизованных коллекций.
Типовая безопасность и строго типизованные коллекции
В мире .NET, существовавшем до появления версии 2.0, программисты попытались решить проблемы типовой безопасности с помощью построения пользовательских строго типизованных коллекций. Для примера предположим, что вы хотите создать пользовательскую коллекцию, которая сможет содержать только объекты типа Person (персона).
Читать дальше