Комментарии 3
у вас в проде нашёлся самый неожиданный боксинг
Самый неожиданный боксинг был по поведению для nullable-типов в современном C#. Так приведение nullable-типа к object при боксинге меняется тип. Например, при приведении Int32? varialble к object боксинг произойдёт только в том случае, если variable был не null. Если был null, то боксинга не будет, просто переменной типа object присвоится null и сведения о изначальном типе потеряются и потом при анбоксинге можно будет привести это значение хоть к Boolean?.
Как только
structприсваивается переменной интерфейсного типа, он боксится — ведь интерфейс ссылочный. Вызовs.Area()идёт уже по упакованной копии. Если вы держитеCircleв локальной переменной конкретного типа и вызываетеArea()напрямую — боксинга нет. Грабли вылезают, когдаstruct“прячут” за интерфейс ради полиморфизма.Лечение: generic-ограничение
where T : struct, IShapeвместо переменной интерфейса — JIT специализирует код по value-типу и вызывает метод без упаковки.
Ссылочность вообще не причем. Структуры прекрасно передаются по ссылке без всяких аллокаций. Проблема в том что каждая передаваемая переменная должна иметь фиксированный заранее известный размер. Если переменная интерфейсного типа то размер фактически передаваемых данных может быть любым до 0 и далее. Не может быть переменной или параметра, размер которого никто не знает при исполнении. В отличии от ссылки размер которой всегда фиксированный. Боксинг это способ впихнуть невпихуемое.
Когда мы пишем ограничение where T : struct, IShape то это совсем другая история, так как это скрывает вызов отдельной предварительно сгенерированной функции для каждого используемого типа. У каждой функции будет свой параметр фиксированного размера.
Боксинг, который уже НЕ боксит: чему научился рантайм
Generic-методы со
struct-аргументом не боксят сам аргумент.EqualityComparer<T>.Default,List<T>,Dictionary<TKey,TValue>специализируются по value-типу: JIT генерирует отдельный нативный код на каждыйstruct-аргумент, и упаковки value→object там нет (при условии корректногоIEquatable<T>— см. пункт 3).
Что значит уже? В C# изначально мономорфизация параметров типов что значит что любой вариант типа с параметром мономорфизируеться в отдельный тип, там нечему бокситься и незачем. Другими словами, не было никакого List<int> всегда был List_int.

Аллокации, которых нет в коде: охота на скрытый боксинг в .NET 10