J'essaie de rassembler toutes les situations dans lesquelles la boxe se produit en C #:
Conversion du type de valeur en
System.Object
type:struct S { } object box = new S();
Conversion du type de valeur en
System.ValueType
type:struct S { } System.ValueType box = new S();
Conversion de la valeur du type d'énumération en
System.Enum
type:enum E { A } System.Enum box = E.A;
Conversion du type de valeur en référence d'interface:
interface I { } struct S : I { } I box = new S();
Utilisation de types valeur dans la concaténation de chaînes C #:
char c = F(); string s1 = "char value will box" + c;
note: les constantes de
char
type sont concaténées au moment de la compilationRemarque: le compilateur depuis la version 6.0 C # Optimise concaténation impliquant
bool
,char
,IntPtr
,UIntPtr
typesCréation d'un délégué à partir d'une méthode d'instance de type valeur:
struct S { public void M() {} } Action box = new S().M;
Appel de méthodes virtuelles non remplacées sur des types de valeur:
enum E { A } E.A.GetHashCode();
Utilisation de modèles constants C # 7.0 sous
is
expression:int x = …; if (x is 42) { … } // boxes both 'x' and '42'!
Boxing dans les conversions de types de tuple C #:
(int, byte) _tuple; public (object, object) M() { return _tuple; // 2x boxing }
Paramètres facultatifs de
object
type avec des valeurs par défaut de type valeur:void M([Optional, DefaultParameterValue(42)] object o); M(); // boxing at call-site
Vérification de la valeur du type générique sans contrainte pour
null
:bool M<T>(T t) => t != null; string M<T>(T t) => t?.ToString(); // ?. checks for null M(42);
note: cela peut être optimisé par JIT dans certains runtimes .NET
Valeur de test de
struct
type de type non contraint ou générique avec les opérateursis
/as
: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);
note: cela peut être optimisé par JIT dans certains runtimes .NET
Y a-t-il d'autres situations de boxe, peut-être cachées, que vous connaissez?
la source
private int? nullableInteger
Réponses:
C'est une excellente question!
La boxe se produit pour exactement une raison: lorsque nous avons besoin d'une référence à un type valeur . Tout ce que vous avez énuméré relève de cette règle.
Par exemple, étant donné que l'objet est un type de référence, la conversion d'un type de valeur en objet nécessite une référence à un type de valeur, ce qui provoque un boxing.
Si vous souhaitez répertorier tous les scénarios possibles, vous devez également inclure des dérivés, tels que le renvoi d'un type de valeur à partir d'une méthode qui renvoie un objet ou un type d'interface, car cela convertit automatiquement le type de valeur vers l'objet / l'interface.
À propos, le cas de concaténation de chaînes que vous avez astucieusement identifié dérive également de la conversion en objet. L'opérateur + est traduit par le compilateur en un appel à la méthode Concat de string, qui accepte un objet pour le type de valeur que vous passez, donc la conversion en objet et donc la boxe se produit.
Au fil des ans, j'ai toujours conseillé aux développeurs de se souvenir de la raison unique de la boxe (que j'ai spécifiée ci-dessus) au lieu de mémoriser chaque cas, car la liste est longue et difficile à retenir. Cela favorise également la compréhension du code IL généré par le compilateur pour notre code C # (par exemple + sur string génère un appel à String.Concat). Lorsque vous avez des doutes sur ce que le compilateur génère et si la boxe se produit, vous pouvez utiliser IL Disassembler (ILDASM.exe). En règle générale, vous devriez rechercher l'opcode box (il n'y a qu'un seul cas où la boxing peut se produire même si l'IL n'inclut pas l'opcode box, plus de détails ci-dessous).
Mais je conviens que certaines occurrences de boxe sont moins évidentes. Vous en avez répertorié une: appeler une méthode non substituée d'un type valeur. En fait, c'est moins évident pour une autre raison: lorsque vous vérifiez le code IL, vous ne voyez pas l'opcode box, mais l'opcode contrainte, donc même dans l'IL ce n'est pas évident que la boxe se produit! Je n'entrerai pas dans le détail exact pourquoi éviter que cette réponse ne devienne encore plus longue ...
Un autre cas de boxe moins évident est lors de l'appel d'une méthode de classe de base à partir d'un struct. Exemple:
struct MyValType { public override string ToString() { return base.ToString(); } }
Ici, ToString est remplacé, donc appeler ToString sur MyValType ne générera pas de boxe. Cependant, l'implémentation appelle la base ToString et cela provoque la boxe (vérifiez l'IL!).
À propos, ces deux scénarios de boxe non évidents découlent également de la règle unique ci-dessus. Lorsqu'une méthode est appelée sur la classe de base d'un type valeur, il doit y avoir quelque chose auquel le mot - clé this doit faire référence. Étant donné que la classe de base d'un type valeur est (toujours) un type référence, le mot - clé this doit faire référence à un type référence, nous avons donc besoin d'une référence à un type valeur et la boxe se produit donc en raison de la règle unique.
Voici un lien direct vers la section de mon cours en ligne .NET qui traite de la boxe en détail: http://motti.me/mq
Si vous n'êtes intéressé que par des scénarios de boxe plus avancés, voici un lien direct (bien que le lien ci-dessus vous y mènera également une fois qu'il abordera les choses plus basiques): http://motti.me/mu
J'espère que ça aide!
Motti
la source
ToString()
est appelé sur un type de valeur particulier qui ne le remplace pas, le type de valeur sera-t-il encadré sur le site d'appel, ou la méthode sera-t-elle envoyée (sans encadrement) vers un remplacement généré automatiquement qui ne fait que chaîner (avec boxe) à la méthode de base?base
un type valeur provoquera une boxe. Cela inclut les méthodes virtuelles qui ne sont pas remplacées par la structure et lesObject
méthodes qui ne sont pas du tout virtuelles (commeGetType()
). Voir cette question .ToString
un mehtodpublic override void ToString() { return base.ToString(); }
et ...ToString()
d'accéder à une méthode struct via Reflection comme n'importe quelle autre et utilisée pour créer un délégué statique qui prend le type struct commeref
paramètre [une telle chose fonctionne avec des méthodes struct non héritées], mais je j'ai juste essayé de créer un tel délégué et cela n'a pas fonctionné. Est-il possible de créer un délégué statique pour laToString()
méthode d' une structure , et si oui, quel doit être le type de paramètre?Appel de la méthode GetType () non virtuelle sur le type de valeur:
struct S { }; S s = new S(); s.GetType();
la source
GetType
nécessite le boxing non seulement parce qu'il n'est pas virtuel, mais parce que les emplacements de stockage de type valeur, contrairement aux objets de tas, n'ont pas de champ "caché" quiGetType()
peut être utilisé pour identifier leur type.Enum
avoir ses propres méthodes non virtuelles, bien qu'uneToString()
méthode pour unEnum
aurait besoin d'avoir accès aux informations de type. Je me demande siObject
,ValueType
ouEnum
possède des méthodes non virtuelles qui pourraient effectuer leur travail sans informations de type.Mentionné dans la réponse de Motti, illustrant simplement avec des exemples de code:
Paramètres impliqués
public void Bla(object obj) { } Bla(valueType) public void Bla(IBla i) //where IBla is interface { } Bla(valueType)
Mais c'est sûr:
public void Bla<T>(T obj) where T : IBla { } Bla(valueType)
Type de retour
public object Bla() { return 1; } public IBla Bla() //IBla is an interface that 1 inherits { return 1; }
Vérification de T sans contrainte par rapport à null
public void Bla<T>(T obj) { if (obj == null) //boxes. }
Utilisation de dynamique
dynamic x = 42; (boxes)
Un autre
enumValue.HasFlag
la source
System.Collections
telles queArrayList
ouHashTable
.Certes, ce sont des exemples spécifiques de votre premier cas, mais ils peuvent être des pièges cachés. C'est incroyable la quantité de code que je rencontre encore aujourd'hui qui utilise ces derniers au lieu de
List<T>
etDictionary<TKey,TValue>
.la source
L'ajout de n'importe quelle valeur de type valeur dans ArrayList provoque un boxing:
ArrayList items = ... numbers.Add(1); // boxing to object
la source