← Назад к вопросам

Что такое Boxing?

1.0 Junior🔥 111 комментариев
#C# и ООП#Управление памятью

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Boxing и Unboxing в C#

Boxing — это механизм преобразования значимого типа (value type) в ссылочный тип (reference type). Это критически важный концепт для понимания производительности в C# и Unity, особенно при работе с коллекциями.

Типы данных в C#

Value Types (Значимые типы):

  • Хранятся в стеке (stack)
  • Включают: int, float, bool, struct, enum
  • Быстрые, не требуют сборки мусора
  • Копируются по значению

Reference Types (Ссылочные типы):

  • Хранятся в куче (heap)
  • Включают: class, string, object, array
  • Требуют сборки мусора
  • Копируются по ссылке

Что такое Boxing?

Boxing — это неявное преобразование value type в object (reference type).

int myInt = 42;        // Value type, хранится в стеке
object boxedInt = myInt; // Boxing! Значение скопировано в heap

Что происходит при Boxing?

  1. Выделяется память в куче для хранения значения
  2. Копируется значение из стека в кучу
  3. Возвращается ссылка на объект в куче
  4. Оригинальное значение остаётся в стеке
// Внутри это примерно так:
int value = 42;
object boxed = new object();
buffer.BlockCopy(value, 0, boxed, offset, sizeof(int));

Unboxing

Unboxing — обратный процесс, преобразование object обратно в value type.

object boxedInt = 42;
int unboxedInt = (int)boxedInt; // Unboxing! Значение скопировано обратно

Где происходит автоматический Boxing?

1. Передача в методы, которые принимают object:

public void PrintValue(object value) {
    Debug.Log(value);
}

int myInt = 42;
PrintValue(myInt); // Автоматический boxing

2. Добавление value type в коллекцию object:

ArrayList list = new ArrayList();
int score = 100;
list.Add(score); // Boxing! Огромная ошибка в современном коде

foreach (object item in list) {
    int value = (int)item; // Unboxing
}

3. Использование в строке с интерполяцией:

int health = 80;
string message = $"Здоровье: {health}"; // Boxing в ToString()

Проблема с Boxing для производительности

Проблема 1: Выделение памяти и сборка мусора

// Плохо: каждый кадр выделяем память и создаём мусор
private void Update() {
    foreach (int score in scores) {
        Debug.Log($"Счёт: {score}"); // Boxing при интерполяции
    }
}

Это может привести к:

  • Фреймдропам из-за GC сборки
  • Утечкам памяти
  • Снижению FPS

Проблема 2: Копирование данных

Boxing требует копирования всех данных из стека в кучу, что медленно.

Как избежать Boxing?

1. Используй Generic коллекции вместо ArrayList:

// Плохо: ArrayList с boxing
ArrayList list = new ArrayList();
list.Add(42);      // Boxing
int value = (int)list[0]; // Unboxing

// Хорошо: List<T> без boxing
List<int> list = new List<int>();
list.Add(42);      // Нет boxing
int value = list[0]; // Нет unboxing

2. Избегай передачи value type в методы с object параметром:

// Плохо
public void Process(object value) { }
Process(42); // Boxing

// Хорошо: используй generics
public void Process<T>(T value) { }
Process(42); // Нет boxing

3. Не используй string интерполяцию в горячих путях:

// Плохо: каждый кадр в Update
private void Update() {
    Debug.Log($"Position: {transform.position}"); // Boxing
}

// Хорошо: кэширование
private void OnDebug() {
    var pos = transform.position;
    Debug.Log($"Position: {pos}"); // Только когда нужно
}

4. Используй string.Concat или StringBuilder:

// Плохо
string result = "Health: " + health.ToString(); // Boxing

// Хорошо
string result = string.Concat("Health: ", health); // Явный контроль

Проверка Boxing в коде

// Пример типичных ошибок в Unity

// 1. Dictionary<string, object>
Dictionary<string, object> data = new Dictionary<string, object>();
data["score"] = 100; // Boxing int
int score = (int)data["score"]; // Unboxing

// Исправление: Dictionary<string, int>
Dictionary<string, int> data = new Dictionary<string, int>();
data["score"] = 100; // Нет boxing
int score = data["score"]; // Нет unboxing

// 2. SendMessage с параметром
gameObject.SendMessage("TakeDamage", 10); // Boxing

// Исправление: прямой вызов метода
enemy.TakeDamage(10);

Практические советы

Правило 1: Всегда используй Generic коллекции (List<T>, Dictionary<K, V>) вместо ArrayList и Hashtable

Правило 2: Профилируй код в Unity Profiler и ищи "Managed Allocations"

Правило 3: Избегай LINQ на горячих путях (он создаёт boxing при работе с value types)

// Плохо: LINQ может вызвать boxing
var result = numbers.Where(n => n > 10).ToList();

// Хорошо: явный цикл
var result = new List<int>();
foreach (var n in numbers) {
    if (n > 10) result.Add(n);
}

Выводы

Boxing — это неявное преобразование value type в object, которое создаёт мусор и снижает производительность. В современном C# и Unity это считается антипаттерном. Используй Generic типы, профилируй код и избегай неявных операций boxing при разработке игр для мобильных платформ.