Подтвердить что ты не робот

Есть ли накладные расходы во время выполнения только для чтения?

По какой-то причине я всегда предполагал, что с полями readonly связаны служебные данные, которые я считал CLR, отслеживая, было ли инициализировано поле readonly или нет. Накладные расходы здесь были бы дополнительным использованием памяти для отслеживания состояния и проверки при назначении значения.

Возможно, я принял это, потому что я не знал, что поле readonly может быть инициализировано только внутри конструктора или внутри самого объявления поля и без проверки времени выполнения, вы не сможете гарантировать, что это не будет назначается несколько раз различными способами. Но теперь я знаю это, это легко может быть статически проверено компилятором С#, не так ли? Так это дело?

Другая причина в том, что я читал, что использование readonly имеет "незначительное" влияние на производительность, но они никогда не входили в это утверждение, и я не могу найти информацию по этому вопросу, поэтому мой вопрос. Я не знаю, какое другое влияние на производительность может быть в стороне от проверок времени выполнения.

Третья причина заключается в том, что я видел, что readonly сохраняется в скомпилированном IL как initonly, поэтому в чем причина того, что эта информация находится в IL, если readonly является не чем иным, как гарантией С#, что поле никогда не назначается вне конструктора или объявления?

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

Итак, я предполагаю, что "readonlyness" - это только функция времени компиляции, может ли кто-нибудь подтвердить/опровергнуть это? И если это так, в чем причина включения этой информации в ИЛ?

4b9b3361

Ответ 1

Вы должны смотреть на него с той же точки зрения, что и модификаторы доступа. Модификаторы доступа существуют в IL, но действительно ли они проверены временем выполнения? (1) Я не могу напрямую назначать частные поля во время компиляции, (2) Я могу назначить их с помощью отражения. До сих пор, кажется, не проверка времени выполнения, как только для чтения.

Но рассмотрим модификаторы доступа. Выполните следующие действия:

  • Создать сборку A.dll с открытым классом C
  • Создайте сборку B.exe, которая ссылается на A.dll. B.exe использует класс C.
  • Постройте две сборки. Запуск B.exe отлично работает.
  • Восстановить A.dll, но установить класс C во внутреннее. Замените A.dll в каталоге B.exe.

Теперь запуск B.exe вызывает исключение во время выполнения.

Модификаторы доступа существуют и в IL, не так ли? Итак, какова их цель? Цель состоит в том, что другие сборки, которые ссылаются на сборку .Net, должны знать, к чему им разрешен доступ, и к чему им не разрешен доступ, как время компиляции, так и время выполнения.

По-видимому, похоже, что в IL. Он сообщает другим сборкам, могут ли они писать в поле определенного типа. Однако readonly не выглядит, чтобы иметь такую ​​же проверку времени выполнения, что и модификаторы доступа, представленные в моем примере выше. Кажется, что readonly является проверкой времени компиляции и не встречается во время выполнения. Взгляните на образец производительности здесь: Производительность только для чтения vs const.

Опять же, это не означает, что ИЛ бесполезен. IL гарантирует, что во время компиляции возникает ошибка. Помните, что когда вы строите, вы не создаете против кода, а сборки.

Ответ 2

Если вы используете стандартную переменную экземпляра, readonly будет почти идентично нормальной переменной. Добавленный IL становится проверкой времени компиляции, но во время выполнения практически игнорируется.

Если вы используете статический член readonly, все немного отличается...

Поскольку статический член readonly задан во время статического конструктора, JIT "знает", что существует значение. Дополнительной памяти нет - readonly просто запрещает другим методам устанавливать это, но это проверка времени компиляции.

SInce JIT знает, что этот член никогда не может измениться, он получает "жестко закодированный" во время выполнения, поэтому конечный эффект аналогичен значению const. Разница в том, что это займет больше времени во время самого JIT-времени, так как компилятору JIT необходимо выполнить дополнительную работу, чтобы укрепить значение readonly на месте. (Это будет очень быстро, хотя.)

Эксперт С++/CLI от Маркуса Хеге имеет достаточно хорошее объяснение этого.

Ответ 3

Один важный момент, еще не упомянутый другими ответами, заключается в том, что при обращении к полю readonly или при доступе к любому свойству запрос выполняется с использованием копии данных. Если данные, о которых идет речь, представляют собой тип значения с более чем 4-8 байтами данных, стоимость этого дополнительного копирования иногда может быть значительным. Обратите внимание, что, несмотря на большой скачок в стоимости, когда структуры растут от 16 до 17, структуры могут быть немного больше и все же быть быстрее, чем классы во многих приложениях, , если они не слишком часто копируются. Например, если предполагается, что у него есть тип, представляющий вершины треугольника в трехмерном пространстве. Прямой реализацией будет структура, содержащая структуру с тремя float для каждой точки; всего 36 байт. Если точки и координаты внутри каждой точки являются изменяемыми общедоступными полями, можно быстро и легко получить доступ к someTriangle.P1.X, не копируя никаких данных, кроме Y-координаты вершины 1. С другой стороны, если P1 было свойство или поле readonly, компилятор должен будет скопировать P1 во временную структуру, а затем прочитать X.

Ответ 4

Даже если только чтение только имело эффект во время компиляции, все равно было бы необходимо сохранить данные в сборке (то есть IL). CLR - это класс Обычный Язык. Время выполнения - классы, написанные на одном языке, могут использоваться и расширяться другими языками.

Поскольку каждый компилятор для CLR не будет знать, как читать и компилировать каждый другой язык, чтобы сохранить семантику полей readonly, эти данные необходимо сохранить в сборке, чтобы компиляторы для других языки будут уважать его.

Конечно, тот факт, что поле отмечено readonly, означает, что JIT может делать другие вещи, такие как оптимизация (например, встроенное использование значения) и т.д. Независимо от того, что вы использовали отражение для изменения значения поля, создавая IL, который изменяет поле initonly вне соответствующего конструктора (экземпляр или статический, в зависимости от типа поля), приведет к непроверяемой сборке.