Frage Boxing Occurrence in C #


Ich versuche, alle Situationen zu sammeln, in denen Boxen in C # stattfindet:

  • Werttyp in konvertieren System.Object Art:

    struct S { }
    object box = new S();
    
  • Werttyp in konvertieren System.ValueType Art:

    struct S { }
    System.ValueType box = new S();
    
  • Wert des Aufzählungstyps in konvertieren System.Enum Art:

    enum E { A }
    System.Enum box = E.A;
    
  • Werttyp in Schnittstellenreferenz konvertieren:

    interface I { }
    struct S : I { }
    I box = new S();
    
  • Verwenden von Werttypen in C # -Kettenverkettung:

    char c = F();
    string s1 = "char value will box" + c;
    

    Hinweis: Konstanten von char type werden zur Kompilierzeit verkettet

    Hinweis: seit Version 6.0 C # -Compiler optimiert die Verkettung involvierend bool, char, IntPtr, UIntPtr Arten

  • Erstellen eines Delegaten aus der Werttypinstanzmethode:

    struct S { public void M() {} }
    Action box = new S().M;
    
  • Nicht überschriebene virtuelle Methoden auf Werttypen aufrufen:

    enum E { A }
    E.A.GetHashCode();
    
  • C # 7.0 konstante Muster unter verwenden is Ausdruck:

    int x = …;
    if (x is 42) { … } // boxes both 'x' and '42'!
    
  • Boxen in C # Tupeltypen Konvertierungen:

    (int, byte) _tuple;
    
    public (object, object) M() {
      return _tuple; // 2x boxing
    }
    
  • Optionale Parameter von object Typ mit Werttyp Standardwerte:

    void M([Optional, DefaultParameterValue(42)] object o);
    M(); // boxing at call-site
    
  • Überprüfen des Werts des unbeschränkten generischen Typs für null:

    bool M<T>(T t) => t != null;
    string M<T>(T t) => t?.ToString(); // ?. checks for null
    M(42);
    

    Hinweis: Dies kann durch JIT in einigen .NET-Laufzeiten optimiert werden

  • Typprüfwert von unbeschränkter oder struct generischer Typ mit is/as Betreiber:

    bool M<T>(T t) => t is int;
    int? M<T>(T t) => t as int?;
    IEquatable<T> M<T>(T t) => t as IEquatable<T>;
    M(42);
    

    Hinweis: Dies kann durch JIT in einigen .NET-Laufzeiten optimiert werden

Gibt es noch weitere Situationen des Boxens, vielleicht verborgen, von denen du weißt?


76
2017-11-03 13:22


Ursprung


Antworten:


Das ist eine gute Frage!

Boxen findet aus genau einem Grund statt: wenn wir einen Verweis auf einen Werttyp benötigen. Alles, was Sie aufgelistet haben, fällt in diese Regel.

Da zum Beispiel Objekt ein Referenztyp ist, erfordert das Umwandeln eines Wertetyps in ein Objekt einen Verweis auf einen Wertetyp, der zum Boxing führt.

Wenn Sie jedes mögliche Szenario auflisten möchten, sollten Sie auch Ableitungen hinzufügen, z. B. einen Werttyp aus einer Methode zurückgeben, die ein Objekt oder einen Schnittstellentyp zurückgibt, da dies den Werttyp automatisch auf das Objekt / die Schnittstelle überträgt.

Übrigens, der String-Verkettung-Fall, den Sie geschickt identifiziert haben, leitet sich auch vom Casting zum Objekt ab. Der Operator + wird vom Compiler in einen Aufruf der Concat-Methode von string übersetzt, die ein Objekt für den Werttyp akzeptiert, den Sie übergeben, sodass ein Casting zum Objekt und damit ein Boxing stattfindet.

Im Laufe der Jahre habe ich Entwicklern immer empfohlen, sich an den einzelnen Grund für das Boxen zu erinnern (ich oben angegeben), anstatt jeden einzelnen Fall auswendig zu lernen, weil die Liste lang und schwer zu merken ist. Dies fördert auch das Verständnis, welchen IL-Code der Compiler für unseren C # -Code generiert (zum Beispiel + on-String führt zu String.Concat). Wenn Sie nicht sicher sind, was der Compiler generiert und ob es zu einem Boxing kommt, können Sie IL Disassembler (ILDASM.exe) verwenden. In der Regel sollten Sie nach dem Box-Opcode suchen (es gibt nur einen Fall, in dem das Boxen auftreten kann, obwohl die IL den Box-Opcode nicht enthält, mehr Details unten).

Aber ich stimme zu, dass einige Box-Ereignisse weniger offensichtlich sind. Sie haben eine davon aufgelistet: eine nicht überschriebene Methode eines Werttyps aufrufen. Tatsächlich ist dies aus einem anderen Grund weniger offensichtlich: Wenn Sie den IL-Code überprüfen, sehen Sie nicht den Box-Opcode, sondern den Constraint-Opcode, und selbst in der IL ist es nicht offensichtlich, dass Boxen stattfindet! Ich werde nicht genau ins Detail gehen, um zu verhindern, dass diese Antwort noch länger wird ...

Ein weiterer Fall für weniger offensichtliches Boxen ist das Aufrufen einer Basisklassenmethode aus einer Struktur. Beispiel:

struct MyValType
{
    public override string ToString()
    {
        return base.ToString();
    }
}

Hier wird ToString überschrieben, sodass das Aufrufen von ToString auf MyValType kein Boxing generiert. Die Implementierung ruft jedoch die Basis ToString auf und das verursacht Boxen (überprüfen Sie die IL!).

Diese beiden nicht naheliegenden Boxszenarien stammen übrigens auch aus der obigen einzigen Regel. Wenn eine Methode für die Basisklasse eines Werttyps aufgerufen wird, muss etwas für die Methode vorhanden sein Dies Stichwort, auf das Bezug genommen wird. Da die Basisklasse eines Werttyps (immer) ein Referenztyp ist, wird der Dies Das Schlüsselwort muss sich auf einen Referenztyp beziehen. Daher benötigen wir einen Verweis auf einen Werttyp, sodass aufgrund der einzelnen Regel ein Boxing erfolgt.

Hier ist ein direkter Link zum Abschnitt meines Online-.NET-Kurses, der das Boxen im Detail behandelt: http://motti.me/mq

Wenn Sie nur an fortgeschrittenen Boxszenarien interessiert sind, hier ist ein direkter Link dort (obwohl der Link oben Sie auch dorthin bringt, sobald es die grundlegenderen Sachen bespricht): http://motti.me/mu

Ich hoffe das hilft!

Motti


40
2017-11-03 15:32



Nicht-virtuelle GetType () -Methode für Werttyp aufrufen:

struct S { };
S s = new S();
s.GetType();

5
2017-11-03 15:20



Erwähnt in Mottis Antwort, nur illustrieren mit Codebeispielen:

Parameter beteiligt

public void Bla(object obj)
{

}

Bla(valueType)

public void Bla(IBla i) //where IBla is interface
{

}

Bla(valueType)

Aber das ist sicher:

public void Bla<T>(T obj) where T : IBla
{

}

Bla(valueType)

Rückgabetyp

public object Bla()
{
    return 1;
}

public IBla Bla() //IBla is an interface that 1 inherits
{
    return 1;
}

Uneingeschränktes T gegen Null prüfen

public void Bla<T>(T obj)
{
    if (obj == null) //boxes.
}

Verwendung von dynamischen

dynamic x = 42; (boxes)

Noch einer

enumValue.HasFlag


1
2018-04-16 09:49



  • Verwenden der nicht generischen Sammlungen in System.Collections sowie ArrayList oder HashTable.

Zugegeben, dies sind spezifische Instanzen Ihres ersten Falles, aber sie können versteckte Fälle sein. Es ist erstaunlich, wie viel Code ich heute noch benutze, anstatt sie zu verwenden List<T> und Dictionary<TKey,TValue>.


0
2017-11-03 15:40



Das Hinzufügen eines Wertes eines Werttyps in die ArrayList bewirkt ein Boxen:

ArrayList items = ...
numbers.Add(1); // boxing to object

0
2017-11-03 21:51