Первая проблема заключается в том, что использование классов коллекций System.Collections и System.Collections.Specialized в результате дает код с низкой производительностью, особенно в случае манипулирования числовыми данными (например, типами значений). Как вы вскоре увидите, когда структуры хранятся в любом необобщенном классе коллекции, прототипированном для оперирования с System.Object, среда CoreCLR должна осуществлять некоторое количество операций перемещения в памяти, что может нанести ущерб скорости выполнения.
Вторая проблема связана с тем, что большинство необобщенных классов коллекций не являются безопасными в отношении типов, т.к. они были созданы для работы с System.Object и потому могут содержать в себе вообще все что угодно. Если разработчик нуждался в создании безопасной в отношении типов коллекции (скажем, контейнера, который способен хранить объекты, реализующие только определенный интерфейс), то единственным реальным вариантом было создание нового класса коллекции вручную. Хотя задача не отличалась высокой трудоемкостью, решать ее было несколько утомительно.
Прежде чем вы увидите, как применять обобщения в своих программах, полезно чуть глубже рассмотреть недостатки необобщенных классов коллекций, что поможет лучше понять проблемы, которые был призван решить механизм обобщений. Создайте новый проект консольного приложения по имени IssuesWithNongenericCollections, импортируйте пространства имен System и System.Collections в начале файла Program.cs и удалите оставшийся код:
using System;
using System.Collections;
Проблема производительности
Как уже было указано в главе 4, платформа .NET Core поддерживает две обширные категории данных: типы значений и ссылочные типы. Поскольку в .NET Core определены две основные категории типов, временами возникает необходимость представить переменную одной категории как переменную другой категории. Для этого в C# предлагается простой механизм, называемый SimpleBoxUnboxOperation() создана локальная переменная типа int. Если где-то в приложении понадобится представить такой тип значения как ссылочный тип, то значение придется
static void SimpleBoxUnboxOperation()
{
// Создать переменную ValueType (int).
int myInt = 25;
// Упаковать int в ссылку на object.
object boxedInt = myInt;
}
Упаковку можно формально определить как процесс явного присваивания данных типа значения переменной System.Object. При упаковке значения среда CoreCLR размещает в куче новый объект и копирует в него величину типа значения (в данном случае 25). В качестве результата возвращается ссылка на вновь размещенный в куче объект.
Противоположная операция также разрешена и называется boxedInt действительно является int:
static void SimpleBoxUnboxOperation()
{
// Создать переменную ValueType (int).
int myInt = 25;
// Упаковать int в ссылку на object.
object boxedInt = myInt;
// Распаковать ссылку обратно в int.
int unboxedInt = (int)boxedInt;
}
Когда компилятор C# встречает синтаксис упаковки/распаковки, он выпускает код CIL, который содержит коды операций box/unbox. Если вы просмотрите сборку с помощью утилиты ildasm.exe, то обнаружите в ней показанный далее код CIL:
.method assembly hidebysig static
void '<
{
.maxstack 1
.locals init (int32 V_0, object V_1, int32 V_2)
IL_0000: nop
IL_0001: ldc.i4.s 25
IL_0003: stloc.0
IL_0004: ldloc.0
IL_0005: box [System.Runtime]System.Int32