Как снижается производительность при понижающем показателе?
Вопрос
Просто пытаюсь разобраться в дженериках, читая эта поучительная статья Джувала Лоуи
Перефразируя..Когда вы определяете определение универсального класса, оно компилируется в IL.
- Для типов значений, как только вы запрашиваете определенный тип значения, он заменяет T вашим конкретным типом значения, чтобы получить IL для этой конкретной конфигурации, например
MyList<int>
Выгода:Никаких штрафов за боксирование и распаковку. - Все хорошо..для ссылочных типов компилятор заменяет все экземпляры T в вашем определении на Object и создает IL, который используется для всех типов ссылок.Экземпляры, однако, распределяются на основе фактического запрошенного типа ссылки, например
MyList<String>
Теперь, до дженериков, мы могли бы написать методы, которые принимают Object
параметры.Дженерики претендуют на 100% улучшение производительности, потому что 'это позволяет избежать снижения производительности, которое вы несете, когда понижаете тип объекта до вашего конкретного типа, когда хотите его использовать'
// assume GetItem returns an Object
string sMyPreciousString = (string) obList.GetItem();
Каково снижение производительности при переходе от объекта к определенному ссылочному типу?Также кажется, что приведение к объекту (даже дженерики могли бы это сделать) не влияет на производительность..почему?
Решение
Преобразование в object не требует проверки времени выполнения - оно всегда будет работать и, по сути, просто не работает.
Понижающая передача требует проверки времени выполнения, чтобы убедиться, что вы, например, не преобразуете поток в Строку.Это довольно небольшой штраф, и вряд ли он станет узким местом, но избежать его - всего лишь одно дополнительное преимущество дженериков.
Другие советы
Снижение производительности связано с необходимостью проверки типа во время выполнения.Если B является подклассом A, то когда вы приводите B в A, вы знаете во время компиляции, что это будет безопасно, поскольку все Bs являются As.Следовательно, вам не нужно генерировать какой-либо код среды выполнения для проверки типа.
Однако, когда вы преобразуете A в B, вы не знаете во время компиляции, является ли A на самом деле B или нет.Это может быть просто A, это может быть тип C, другой подтип A.Следовательно, вам нужно сгенерировать код времени выполнения, который удостоверится, что объект на самом деле является B, и выдаст исключение, если это не так.
У дженериков нет этой проблемы, потому что компилятор знает во время компиляции, что в структуру данных были введены только Bs, поэтому, когда вы извлекаете что-то, компилятор знает, что это будет B, поэтому нет необходимости в проверке типа во время выполнения.