C# — Boxing et Unboxing


1. Définition du boxing

Le boxing est la conversion d’un type valeur (int, bool, double, struct) en un object ou une interface. Le runtime copie la valeur vers le tas (heap).

int x = 42;
object obj = x; // boxing automatique

Le boxing est implicite et peut créer des allocations inutiles.

2. Définition de l’unboxing

L’unboxing est l’opération inverse : extraire la valeur depuis l’objet boxé. Il doit être explicite car C# exige le type exact.

object obj = 42;
int y = (int)obj; // unboxing

3. Cas où le boxing se produit

3.1 Affecter un type valeur à un object

object o = 10; // boxing

3.2 Passer un type valeur à une méthode qui attend object

void Print(object o) => Console.WriteLine(o);
Print(123); // boxing

3.3 Collections non génériques (ArrayList)

ArrayList list = new ArrayList();
list.Add(5); // boxing

3.4 Struct → interface

public interface IPrintable { void Print(); }

public struct Point : IPrintable {
    public int X, Y;
    public void Print() => Console.WriteLine($"{X},{Y}");
}

IPrintable p = new Point( ); // boxing

4. Cas avancés (interfaces + structs)

4.1 Boxing lors d’un appel de méthode via interface

IPrintable p = new Point { X = 10, Y = 20 };
p.Print(); // boxing automatique

L’appel via une interface exige une référence → boxing.

4.2 Boxing dans une méthode générique contrainte par interface

public static void Call(T value) where T : IPrintable {
    value.Print(); // boxing si T est une struct
}

4.3 Version sans boxing (méthode d’extension generics + struct)

public static void PrintFast(this T v)
    where T : struct, IPrintable
{
    v.Print(); // aucun boxing !
}

5. Structs + interface générique → double boxing

public interface IWrapper { T Value { get; } }

public struct Wrapper : IWrapper {
    public T Value { get; init; }
}

Wrapper w = new Wrapper { Value = 10 };

IWrapper iw = w; // boxing
int v = iw.Value;     // unboxing du T

6. Exemple d'erreur classique : mauvais unboxing

object o = 42;

// ❌ InvalidCastException
double d = (double)o;

L’unboxing doit récupérer le type exact.

7. Impact performance

Un million de boxings peut multiplier le temps par 30 à 100.

public interface ICompute {
    int Compute(int x);
}

public struct AddOne : ICompute {
    public int Compute(int x) => x + 1;
}

public int Work(T algo) where T : ICompute {
    int sum = 0;
    for (int i = 0; i < 1_000_000; i++)
        sum += algo.Compute(i); // BOXING à chaque appel !
    return sum;
}

Version optimisée

public int Work(T algo) where T : struct, ICompute {
    int sum = 0;
    for (int i = 0; i < 1_000_000; i++)
        sum += algo.Compute(i); // aucun boxing
    return sum;
}

8. Comment éviter le boxing

9. Résumé visuel

Boxing : struct → object / interface (implicite)
Unboxing : object → struct (explicite + cast)
Problème : allocations, lenteur, GC, bugs
Solution : generics, struct constraints