Ich versuche, alle Situationen zu erfassen, in denen Boxen in C # auftritt:
Werttyp in Typ
System.Object
konvertieren:struct S { } object box = new S();
Werttyp in Typ
System.ValueType
konvertieren:struct S { } System.ValueType box = new S();
Konvertieren des Werts des Aufzählungstyps in einen
System.Enum
Typ:enum E { A } System.Enum box = E.A;
Werttyp in Schnittstellenreferenz konvertieren:
interface I { } struct S : I { } I box = new S();
Verwenden von Werttypen bei der Verkettung von C # -Strings:
char c = F(); string s1 = "char value will box" + c;
Hinweis: Konstanten vom
char
Typ werden beim Kompilieren verkettetAnmerkung: seit Version 6.0 C # Compiler optimiert Verkettung beteiligt
bool
,char
,IntPtr
,UIntPtr
ArtenErstellen eines Delegaten aus einer Instanzmethode mit Werttyp:
struct S { public void M() {} } Action box = new S().M;
Aufrufen nicht überschriebener virtueller Methoden für Werttypen:
enum E { A } E.A.GetHashCode();
Verwenden von C # 7.0-Konstantenmustern unter
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 vom
object
Typ mit Standardwerten für den Werttyp:void M([Optional, DefaultParameterValue(42)] object o); M(); // boxing at call-site
Überprüfen des Werts des nicht eingeschränkten generischen Typs auf
null
:bool M<T>(T t) => t != null; string M<T>(T t) => t?.ToString(); // ?. checks for null M(42);
Hinweis: Dies kann in einigen .NET-Laufzeiten durch JIT optimiert werden
Typprüfwert vom uneingeschränkten oder
struct
generischen Typ mitis
/as
Operatoren: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 in einigen .NET-Laufzeiten durch JIT optimiert werden
Gibt es weitere Situationen des Boxens, vielleicht versteckt, von denen Sie wissen?
quelle
private int? nullableInteger
Antworten:
Das ist eine gute Frage!
Boxen tritt aus genau einem Grund auf: wenn wir einen Verweis auf einen Werttyp benötigen . Alles, was Sie aufgelistet haben, fällt unter diese Regel.
Da Objekt beispielsweise ein Referenztyp ist, erfordert das Umwandeln eines Werttyps in ein Objekt einen Verweis auf einen Werttyp, was zu Boxen führt.
Wenn Sie alle möglichen Szenarien auflisten möchten, sollten Sie auch Ableitungen einschließen, z. B. die Rückgabe eines Werttyps von einer Methode, die ein Objekt oder einen Schnittstellentyp zurückgibt, da dadurch der Werttyp automatisch in das Objekt / die Schnittstelle umgewandelt wird.
Übrigens ergibt sich der von Ihnen scharfsinnig identifizierte Fall der Verkettung von Zeichenfolgen auch aus der Umwandlung in ein Objekt. Der Operator + wird vom Compiler in einen Aufruf der Concat-Zeichenfolgenmethode übersetzt, die ein Objekt für den von Ihnen übergebenen Werttyp akzeptiert, sodass eine Umwandlung in ein Objekt und damit ein Boxen erfolgt.
Im Laufe der Jahre habe ich Entwicklern immer geraten, sich an den einzigen Grund für das Boxen zu erinnern (den ich oben angegeben habe), anstatt sich jeden einzelnen Fall zu merken, da die Liste lang und schwer zu merken ist. Dies fördert auch das Verständnis dessen, welchen IL-Code der Compiler für unseren C # -Code generiert (z. B. + on string führt zu einem Aufruf von String.Concat). Wenn Sie Zweifel haben, was der Compiler generiert, und wenn Boxen auftritt, können Sie IL Disassembler (ILDASM.exe) verwenden. Normalerweise sollten Sie nach dem Box-Opcode suchen (es gibt nur einen Fall, in dem Boxen auftreten kann, obwohl die IL den Box-Opcode nicht enthält, weitere Details siehe unten).
Aber ich stimme zu, dass einige Boxereignisse weniger offensichtlich sind. Sie haben eine davon aufgelistet: Aufrufen einer nicht überschriebenen Methode eines Werttyps. In der Tat 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. Selbst in der IL ist es nicht offensichtlich, dass Boxen stattfindet! Ich werde nicht ins Detail gehen, warum ich verhindern soll, dass diese Antwort noch länger wird ...
Ein weiterer Fall für weniger offensichtliches Boxen ist das Aufrufen einer Basisklassenmethode aus einer Struktur. Beispiel:
Hier wird ToString überschrieben, sodass das Aufrufen von ToString auf MyValType kein Boxen erzeugt. Die Implementierung ruft jedoch den Basis-ToString auf und dies führt zu Boxen (überprüfen Sie die IL!).
Übrigens leiten sich diese beiden nicht offensichtlichen Boxszenarien auch aus der obigen Einzelregel ab. Wenn eine Methode für die Basisklasse eines Werttyps aufgerufen wird, muss das Schlüsselwort this auf etwas verweisen. Da die Basisklasse eines Werttyps (immer) ein Referenztyp ist, muss dieses Schlüsselwort auf einen Referenztyp verweisen. Daher benötigen wir einen Verweis auf einen Werttyp, sodass das Boxen aufgrund der einzelnen Regel auftritt.
Hier ist ein direkter Link zu dem Abschnitt meines Online-.NET-Kurses, in dem das Boxen ausführlich behandelt wird: http://motti.me/mq
Wenn Sie nur an fortgeschritteneren Boxszenarien interessiert sind, finden Sie hier einen direkten Link (obwohl der obige Link Sie auch dorthin führt, sobald er die grundlegenderen Dinge bespricht): http://motti.me/mu
Ich hoffe das hilft!
Motti
quelle
ToString()
für einen bestimmten Werttyp aufgerufen wird, der ihn nicht überschreibt, wird der Werttyp am Aufrufstandort eingerahmt oder die Methode (ohne Boxing) an eine automatisch generierte Überschreibung gesendet, die nur eine Kette (mit) ausführt Boxen) zur Basismethode?base
einen Werttyp aufruft, führt zum Boxen. Dies umfasst virtuelle Methoden, die nicht von der Struktur überschrieben werden, undObject
Methoden, die überhaupt nicht virtuell sind (wieGetType()
). Siehe diese Frage .ToString
ein Mehtod nicht überschreibtpublic override void ToString() { return base.ToString(); }
und ...ToString()
Methode einer Struktur wie auf jede andere über Reflection zugegriffen und ein statischer Delegat erstellt werden, der den Strukturtyp alsref
Parameter verwendet [so etwas funktioniert mit nicht vererbten Strukturmethoden], aber ich Ich habe gerade versucht, einen solchen Delegaten zu erstellen, und es hat nicht funktioniert. Ist es möglich, einen statischen Delegaten für dieToString()
Methode einer Struktur zu erstellen , und wenn ja, wie sollte der Parametertyp lauten?Aufrufen der nicht virtuellen GetType () -Methode für den Werttyp:
quelle
GetType
erfordert Boxen nicht nur, weil es nicht virtuell ist, sondern weil Wertspeicherorte im Gegensatz zu Heap-Objekten kein "verstecktes" Feld haben,GetType()
mit dem sie ihren Typ identifizieren können.Enum
, eigene nicht virtuelle Methoden zu haben , obwohl eineToString()
Methode für einEnum
Zugriff auf Typinformationen benötigen würde. Ich frage mich, obObject
,ValueType
oderEnum
hat nicht virtuelle Methoden, die ihre Jobs ohne Typinformationen ausführen könnten.Erwähnt in Mottis Antwort, nur zur Veranschaulichung mit Codebeispielen:
Beteiligte Parameter
Aber das ist sicher:
Rückgabetyp
Unbeschränktes T gegen Null prüfen
Verwendung von Dynamik
Noch einer
enumValue.HasFlag
quelle
System.Collections
wieArrayList
oderHashTable
.Zugegeben, dies sind bestimmte Fälle Ihres ersten Falls, aber es können versteckte Fallstricke sein. Es ist erstaunlich, wie viel Code mir heute noch begegnet, der diese anstelle von
List<T>
und verwendetDictionary<TKey,TValue>
.quelle
Das Hinzufügen eines Werttyps zur ArrayList führt zum Boxen:
quelle