Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ответы ОСиСП.doc
Скачиваний:
68
Добавлен:
11.05.2015
Размер:
1.78 Mб
Скачать

«Распухание» кода

При JIT-компиляции метода, в котором используются обобщенные параметры-типы, CLR подставляет в IL-код метода указанные аргументы-типы, а затем создает машинный код для данного метода, работающего с конкретными типами данных. Это именно то, что нужно, и это одна из основных функций обобщения. Но в таком подходе есть один недостаток: CLR генерирует машинный код для каждого сочетания «метод + тип», что приводит к распуханию кода, и в итоге существенно увеличивается рабочий набор приложения и производительность ухудшается.

В CLR есть несколько оптимизационных алгоритмов, призванных предотвратить разрастание кода.

Если метод вызывается для конкретного аргумента-типа и позже он вызывается опять с тем же аргументом-типом, CLR компилирует код для такого сочетания «метод + тип» только один раз. Поэтому, если List<DateTime> используется в двух совершенно разных сборках (загруженных в один домен AppDomain), CLR компилирует методы для List<DateTime> всего один раз. Это существенно снижает распухание кода.

При использовании другого алгоритма оптимизации CLR считает все аргументы ссылочного типа тождественными, что обеспечивает совместное использование кода. Например, код, скомпилированный в CLR для методов List<String>, может применяться для методов List<Stream>, потому что String и Stream — ссылочные типы. По сути, для всех ссылочных типов используется одинаковый код. CLR выполняет эту оптимизацию, потому что все аргументы и переменные ссылочного типа — это просто указатели (32-разрядное значение в 32-разрядной и 64-разрядное значение в 64-разрядной версии Windows) на объекты в куче, а все указатели на объекты обрабатываются одинаково.

Но если аргументы-типы имеют значимый тип, CLR должна сгенерировать машинный код именно для этого значимого типа. Это объясняется тем, что у значимых типов может быть разный размер. И даже если у двух значимых типов одинаковый размер (например, Int32 и UInt32 — 32-разрядные значения), CLR все равно не может использовать для них один код, потому что для обработки этих значений могут применяться различные машинные команды.

Обобщенные интерфейсы

Основное преимущество обобщений — в их способности определять обобщенные ссылочные и значимые типы. Но для CLR также исключительно важна поддержка обобщенных интерфейсов. Без них любая попытка работы со значимым типом через необобщенный интерфейс (например, IComparable) всякий раз будет приводить к упаковке и потере безопасности типов в процессе компиляции. Это сильно сузило бы применение обобщенных типов. Вот почему CLR поддерживает обобщенные интерфейсы.

Ссылочный и значимый тип реализуют обобщенный интерфейс путем задания аргументов-типов, или же любой тип реализует обобщенный интерфейс, не определяя аргументы-типы. Рассмотрим несколько примеров.

Вот определение обобщенного интерфейса из библиотеки FCL (из пространства имен System.Collections.Generic):

public interface IEnumerator<T> : IDisposable, IEnumerator

{

T Current { get; }

}

А этот тип реализует данный обобщенный интерфейс и задает аргументы-типы. Обратите внимание, что объект Triangle может перечислять набор объектов Point, а тип свойства Current - Point.

internal sealed class Triangle : IEnumerator<Point>

{

private Point[] m_vertices;

// Тип свойства Current в IEnumerator<Point> - Point.

Point Current { get { ... } }

}

Теперь рассмотрим пример типа, реализующего тот же обобщенный интерфейс, но без задания аргументов-типов.

internal sealed class ArrayEnumerator<T> : IEnumerator<T>

{

private T[] m_array;

// Тип свойства Current в IEnumerator<T> - Т.

T Current { get { ... } }

}

Заметьте: объект ArrayEnumerator перечисляет набор объектов T (где T не задано, поэтому код, использующий обобщенный тип ArrayEnumerator, может задать тип T позже). В этом примере свойство Current имеет неопределенный тип данных Т.