Cas d'utilisation pour boxer un type de valeur en C #?
-
06-07-2019 - |
Question
Il existe des cas où une instance d'un type de valeur doit être traité comme un instance d'un type de référence. Pour Des situations comme celle-ci, un type de valeur instance peut être converti en un instance de type de référence à travers un processus appelé boxe. Quand une valeur l'instance de type est encadrée, le stockage est alloué sur le tas et la La valeur de l'instance est copiée dans cette espace. Une référence à ce stockage est placé sur la pile. La valeur en boîte est un objet, un type de référence qui contient le contenu de la valeur type instance.
Dans Wikipedia , il existe un exemple pour Java. Mais en C #, dans certains cas, il faudrait encadrer un type de valeur. Ou une question meilleure / similaire serait: pourquoi voudrait-on stocker un type de valeur sur le tas (encadré) plutôt que sur la pile?
La solution
En général, vous voudrez généralement éviter de mettre en boîte vos types de valeur.
Cependant, il existe de rares occasions où cela est utile. Si vous devez cibler le framework 1.1, par exemple, vous n’avez pas accès aux collections génériques. Toute utilisation des collections dans .NET 1.1 nécessiterait de traiter votre type de valeur en tant que System.Object, ce qui entraînerait la boxing / unboxing.
Il existe encore des cas pour que cela soit utile dans .NET 2.0+. Chaque fois que vous souhaitez tirer parti du fait que tous les types, y compris les types de valeur, peuvent être traités directement en tant qu'objet, vous devrez peut-être utiliser boxing / unboxing. Cela peut être utile parfois, car cela vous permet de sauvegarder n'importe quel type dans une collection (en utilisant object au lieu de T dans une collection générique), mais en général, il est préférable d'éviter cela, car vous perdez la sécurité du type. Le seul cas où la boxe se produit fréquemment, cependant, est lorsque vous utilisez la réflexion - de nombreux appels en réflexion nécessiteront une boxe / unboxing lors de l'utilisation de types de valeur, car le type n'est pas connu à l'avance.
Autres conseils
Il n’existe presque jamais de bonne raison de sceller délibérément un type de valeur. Presque toujours, la raison d'encadrer un type de valeur est de le stocker dans une collection qui n'est pas sensible au type. La vieille ArrayList , par exemple, est une collection. d'objets, qui sont des types de référence. Le seul moyen de collecter, par exemple, des entiers, consiste à les mettre en boîte en tant qu’objets et à les transmettre à ArrayList.
De nos jours, nous avons des collections génériques, ce qui pose moins de problèmes.
La boxe se produit généralement automatiquement dans .NET quand ils le doivent. Souvent, lorsque vous passez un type de valeur à quelque chose qui attend un type de référence. String.Format () est un exemple courant. Lorsque vous transmettez des types de valeur primitifs à cette méthode, ils sont mis en boîte dans le cadre de l'appel. Donc:
int x = 10;
string s = string.Format( "The value of x is {0}", x ); // x is boxed here
Ceci illustre un scénario simple dans lequel un type de valeur (x) est automatiquement mis en boîte pour être passé à une méthode qui attend un objet. En règle générale, vous voulez éviter les types de valeur de boxe lorsque cela est possible ... mais dans certains cas, cela est très utile.
Fait intéressant, lorsque vous utilisez des génériques dans .NET, les types de valeur ne sont pas encadrés lorsqu'ils sont utilisés en tant que paramètres ou membres du type. Ce qui rend les génériques plus efficaces que les anciens codes C # (tels que ArrayList) qui traitent tout comme {objet} pour qu’il soit indépendant du type. Ceci ajoute une raison supplémentaire d'utiliser des collections génériques, telles que List<T>
ou Dictionary<T,K>
sur ArrayList
ou Hashtable
.
Je vous recommanderais 2 beaux articles d'Eric Lippert
http : //blogs.msdn.com/ericlippert/archive/2009/04/27/the-stack-is-an-implementation-detail.aspx
Voici la citation avec laquelle je suis d'accord à 100%
Utilisation de la pile pour les locales de valeur le type est juste une optimisation que le CLR effectue en votre nom. La caractéristique pertinente des types de valeur est qu'ils ont la sémantique d'être copié par valeur, pas que parfois leur désallocation peut être optimisée par le runtime.
Dans 99% des applications, les développeurs ne devraient pas se soucier de savoir pourquoi les types Value sont empilés et non dans le tas, et quel gain de performances pourrions-nous avoir ici. Juts a en tête des règles très simples:
- Évitez de boxer / déballer sinon nécessaire, utilisez des collections de génériques. La plupart des problèmes ne surviennent pas lorsque vous définir vos propres types, mais quand vous utiliser les types existants de manière inappropriée (défini par Microsoft ou votre collègues)
- Faites vos types de valeur simple. Si vous avez besoin d'une structure avec 10-20 champs, je suppose que vous mieux créer une classe. Imagine, tous que les champs seront copiés à chaque fois quand vous passez de temps en temps un fonction par valeur ...
- Je ne pense pas qu'il soit très utile d'avoir types de valeur avec type de référence champs à l'intérieur. Comme struct avec Chaîne et champs d'objet.
- Définissez le type dont vous avez besoin en fonction de fonctionnalité requise, pas sur il devrait être stocké. Les structures ont fonctionnalité limitée comparant à classes, donc si struct ne peut pas fournir la fonctionnalité requise, comme constructeur par défaut, définir la classe.
- Si quelque chose peut en exécuter actions avec les données d'autres types, il est généralement défini comme un classe. Pour les opérations de struct avec différents types doivent être définis que si vous pouvez lancer un type à un autre. Dites que vous pouvez ajouter int à doubler parce que vous pouvez lancer int à double.
- Si quelque chose doit être sans état, c'est une classe.
- Lorsque vous hésitez, utilisez des types de référence. : -)
Toutes les règles permettent des exclusions dans des cas particuliers, mais n'essayez pas de trop optimiser.
p.s. J'ai rencontré des développeurs ASP.NET avec 2 ou 3 années d'expérience qui ne connaissaient pas la différence entre pile et tas. :-( Je ne ferais pas appel à une telle personne si je suis un intervieweur, mais pas parce que la boxe / le déballage peuvent être un goulot d'étranglement dans l'un des sites ASP.NET que j'ai vus.
Je pense qu'un bon exemple de boxe en c # apparaît dans les collections non génériques telles que ArrayList .
Par exemple, lorsqu'une méthode prend un paramètre d'objet et qu'un type de valeur doit être passé.
Ci-dessous quelques exemples de boxe / déballage
ArrayList ints = new ArrayList();
myInts.Add(1); // boxing
myInts.Add(2); // boxing
int myInt = (int)ints [0]; // unboxing
Console.Write("Value is {0}", myInt); // boxing
Une des situations dans lesquelles cela se produit est par exemple si vous avez une méthode qui attend un paramètre de type objet et que vous passez dans l’un des types primitifs, int par exemple. Ou si vous définissez le paramètre comme 'ref' de type int.
Le code
int x = 42;
Console.Writeline("The value of x is {0}", x );
en fait, box et unboxes parce que Writeline
effectue un int
casting à l'intérieur. Pour éviter cela, vous pourriez le faire
int x = 42;
Console.Writeline("The value of x is {0}", x.ToString());
Méfiez-vous des insectes subtils!
Vous pouvez déclarer vos propres types de valeur en déclarant votre propre type comme struct
. Imaginez que vous déclariez un ArrayList
avec beaucoup de propriétés, puis que vous placiez des instances dans un []
. Cela les encadre bien sûr. Référencez-en un maintenant avec l'opérateur readonly
, en le convertissant en type et en définissant une propriété. Vous venez de définir une propriété sur une copie . Celui du <=> n’a toujours pas été modifié.
Pour cette raison, les types de valeur doivent toujours être immuables, c’est-à-dire rendre toutes les variables membres <=> afin qu’elles ne puissent être définies que dans le constructeur et ne possèdent aucun type mutable en tant que membres.