Boxing je konverze value type (například int) na reference type (object) — zabalení hodnoty do objektu na heap. Unboxing je opak (extrahování hodnoty zpět). Obě operace mají cenu na výkon (alokace heap, kopírování), a jejich pochopení vám pomůže vyhnout se zbytečné režii.
Boxing a unboxing v praxi
int number = 42; // a value type (on the stack)
object boxed = number; // BOXING — wraps 42 in an object ON THE HEAP (allocation + copy)
int unboxed = (int)boxed; // UNBOXING — extracts the value back (copy, requires a cast)
Boxing alokuje nový objekt na heap a zkopíruje hodnotu do něj; unboxing ji zkopíruje zpět. Každá boxing operace je alokace na heap — levná jednou, ale drahá v kritických částech kódu nebo smyčkách.
Kdy se boxing stane (často neviditelně)
// ❌ using a value type where an `object` is expected → implicit boxing
object obj = 42; // boxing
ArrayList list = new(); list.Add(42); // boxes (non-generic — stores object)
string s = string.Format("{0}", 42); // boxing for the object[] params
// these can box repeatedly in loops → significant GC pressure
Boxing se často stane implicitně — kdykoliv se value type používá tam, kde je očekáván object (nebo non-generické rozhraní). Ve smyčkách nebo kritických cestách повторený boxing vytváří mnoho krátkodobých objektů na heap, což poškozuje výkon.
Vyhýbání se boxingu: generiky
// ❌ non-generic collection — boxes every value type added
ArrayList list = new();
list.Add(42); // boxes
// ✅ generic collection — NO boxing, type-safe
List<int> list = new();
list.Add(42); // no boxing — stored as int directly
Hlavní řešení je použití generik (List<int> místo ArrayList) — generiky ukládají value types přímo bez boxingu. Toto je jeden z klíčových výhod generik pro výkon oproti starým non-generickým kolekcím.
Proč na tom záleží
Pochopení boxingu a unboxingu je důležité pro psaní výkonného C# a pro pochopení, proč generiky záleží.
Boxing — konverze value type na reference type (object) — způsobuje alokaci na heap a kopírování, a unboxing zkopíruje hodnotu zpět, takže obě operace mají cenu na výkon.
I když je jedna boxing operace levná, problém je v tom, že boxing se často stane implicitně (kdykoliv se value type používá tam, kde je očekáván object) a může se vyskytnout opakovaně ve smyčkách nebo kritických cestách, čímž vytváří mnoho krátkodobých objektů na heap, které zvyšují tlak na garbage collector a degradují výkon — jde o jemný, lehko přehlédnutelný problém s výkonem.
Klíčová praktická poučka je, že generiky se vyhýbají boxingu: použití List<int> místo starého non-generického ArrayList ukládá value types přímo bez boxingu, což je jedna z hlavních výhod generik pro výkon oproti starým non-generickým kolekcím (a klíčový důvod pro vždy používání generických kolekcí).
Pochopení toho, co boxing/unboxing jsou, kdy se stane (často neviditelně), jejich cena na výkon (alokace, tlak na GC, zvláště ve smyčkách) a jak je generiky zabraňují, je důležité pro psaní efektivního C# a pro pochopení, proč jsou generické kolekce preferovány.
Protože je boxing běžným, jemným zdrojem výkonové režie a protože jeho vyhýbání se (prostřednictvím generik) je základní výkonnostní praktikou, je pochopení boxingu/unboxingu cenné, prakticky relevantní znalosti, které odlišují výkonově vědomé vývojáře v C# a je časté téma v pohovorech odrážející pochopení value/reference type modelu a výhod generik.
