Упаковка это процесс помещения объекта из стека (значимого типа) в кучу, если поэтапно:
- Для объекта выделяется место в куче, это место для указателя на типовой объект и индекса синхронизации + длина значимого типа
- Происходит копирование объекта в выделенное место
- Возвращается ссылка на этот объект При распаковке выполняется меньше действий, только взятие адресов у полей структуры, чтобы в даньшей скопировать их в стек. Распаковкой называется именно взятие адресов, а не копирование, поэтому распаковка это более легкая операция.
Альтернатива только распаковать его, изменить и снова упаковать но тогда старый объект останется в куче и будет собран сборщиком мусора, т.е память будет выделена дважды:
var myStruct = new MyStruct {Name = "name"};
var obj = (object) myStruct; // box
var unBoxMyStruct = (MyStruct) obj; // unbox
unBoxMyStruct.Name = "noname";
var newobj = (object) unBoxMyStruct; //box
Console.WriteLine(((MyStruct) obj).Name == "name"); //true
Console.WriteLine(((MyStruct) newobj).Name == "noname"); //true
Но если структура реализует интерфейс IHasName можно сделать так:
var myStruct = new MyStruct {Name = "name"};
var obj = (object) myStruct; // box
((IHasName) obj).Name = "noname";
Console.WriteLine(((MyStruct)obj).Name == "noname"); //true
При касте к интерфейсу упакованного объекта не происходит распаковки. Если же кастить значимый тип к интерфейсу происходит упаковка Таким образом только выполняя каст к интерфейсу можно изменить объект значимого типа
Когда у структуры вызывается базовый метод класса ValueType или Object - она упаковывается(почему?), но если она переопределяет виртуальный метод ValueType или Object, то упаковки не происходит, потому что компилятор понимает, что этот метод никто не может переопределелить (т.к нельзя унаследоватьс от структуры). Также если при переопределении вызывается метод базового класса ValueType или Object, происходит упаковка?
У структур хранящихся в стеке нет ссылки на базовый тип, но тем не менее они могут вызывать методы ValueType'а или Object'a (но при их вызове происходит упаковка). Упаковка происходит из-за того, что методы базовых классов ожидают, что this - это ссылка на объект, т.e ссылочный тип, т.е для того, чтобы методы базовых классов работали, необходимо, скастить упаковать вызываемые объекты, тем не менее если структура переопределяет эти методы и при этом не вызывает базовые - упаковка не происходит, потому что компилятор запечатывает эти методы, так , как будто это методы принадлежат типу в котором переопределены, это логично, потому что структура не может наследовать кому-либо. Таким образом при любом вызове базового метода классов ValueType или Object будет упаковка, при их переопределении и вызове - упаковки не будет Причем в IL коде вызова этих
Да, и дает возможность менять значения упакованного объекта, однако Рихтер непоощряет такой подход, поскольку считает, что структура должна служить только для хранения информации(быть неизменяемой), тогда проблем с упаковкой/распаковкой не возникнет
При вызове виртуального метода компилятору приходится смотреть на реальный тип объекта, т.е приходится подниматься к созданию объекта, и после этого по иерархии и искать самый "нижний"(в плане иерархии,метод). При вызове обычного метода компилятор просто смотрит на текущий тип объекта и вызывает его метод(Вернуться и дописать после прочтения 195 страниц)
Она может происходить тогда, когда объект приводится структура приводится к типу Object или ValueType или любому своему интерфейсу. Либо примитивный тип приводится к Object. Это может быть неожиданно, например:
int t = 12;
Console.WriteLine(t + "end");
В этом случае происходит упаковка типа int в тип object, а потом вызов метода Console.WriteLine(object o1,object o2);
Только у объектов в куче есть блок синхронизации, с которым взаимодействует инструкция lock, а значимые типы хранятся в стеке, который для каждого потока свой, и происходит просто заталкивание этой переменной в стек