71 votes

Quand utiliser des pointeurs en C # / .NET?

Je sais que C # donne au programmeur la possibilité d'accéder, d'utiliser des pointeurs dans un contexte dangereux. Mais quand est-ce nécessaire?

Dans quelles circonstances, l'utilisation de pointeurs devient inévitable?

Est-ce seulement pour des raisons de performance?

Aussi, pourquoi C # expose-t-il cette fonctionnalité via un contexte non sécurisé et en supprime-t-il tous les avantages gérés? Est-il possible d'utiliser des pointeurs sans perdre théoriquement les avantages de l'environnement géré?

88voto

Eric Lippert Points 300275

Quand est-ce nécessaire? Dans quelles circonstances utiliser les pointeurs devient inévitable?

Lorsque le coût net de gestion, à la sécurité de la solution est inacceptable, mais le coût net d'une dangereuse solution est acceptable. Vous pouvez déterminer le coût net ou bénéfice net en soustrayant le total des prestations de l'ensemble des coûts. Les avantages d'une solution dangereux sont des choses comme "pas de perte de temps inutiles exécution des vérifications pour s'assurer de l'exactitude"; les coûts sont (1) d'avoir à écrire du code qui est sûr, même avec la gestion de la sécurité système éteint, et (2) d'avoir à traiter avec ce qui pourrait le rendre le garbage collector de moins en moins efficace, car il ne peut pas se déplacer autour de la mémoire avec un pointeur non géré en elle.

Ou, si vous êtes la personne qui écrit sur la formation de la couche.

Est-ce uniquement pour des raisons de performances?

Il semble pervers d'utiliser des pointeurs dans un langage managé pour des raisons autres que la performance.

Vous pouvez utiliser les méthodes de la classe Marshal pour traiter interopérabilité avec du code non managé dans la grande majorité des cas. (Il peut y avoir quelques cas dans lesquels il est difficile, voire impossible, l'utilisation de la composition des trains d'engrenage à résoudre le problème de l'interopérabilité, mais je ne sais pas du tout.)

Bien sûr, comme je l'ai dit, si vous êtes la personne qui écrit le Maréchal de classe alors évidemment, vous ne devez pas utiliser sur la formation de la couche de résoudre votre problème. Dans ce cas, vous auriez besoin de le mettre en œuvre à l'aide de pointeurs.

Pourquoi est-ce que C# exposer cette fonctionnalité par le biais d'un dangereux contexte, et de supprimer toutes les gérée avantages d'elle?

Ceux gérés avantages viennent avec des coûts de l'exécution. Par exemple, chaque fois que vous demandez un tableau pour son dixième élément, le runtime doit faire une vérification pour voir si il y a un dixième élément, et de lever une exception si il n'y en a pas. Avec des pointeurs d'exécution coût est éliminé.

Le développeur correspondant coût, c'est que si vous le faites mal, alors vous aurez à faire face à une corruption de mémoire de bugs que les formats de votre disque dur et des plantages de votre processus d'une heure plus tard, plutôt que de traiter avec une belle nettoyer l'exception au moment de l'erreur.

Est-il possible d'utiliser des pointeurs sans perdre les avantages de la gestion de l'environnement, en théorie?

Par "avantages" je suppose que vous voulez dire des avantages tels que la collecte des ordures, le type de la sécurité et de l'intégrité référentielle. Donc votre question est essentiellement "est en théorie possible de désactiver le système de sécurité, mais encore obtenir les avantages du système de sécurité est activé?" Non, il n'est manifestement pas. Si vous éteignez que le système de sécurité, parce que vous n'aimez pas la façon dont elle est chère, alors vous n'obtenez pas les avantages d'être sur!

18voto

SLaks Points 391154

Les pointeurs sont une contradiction inhérente à l'gérées, les ordures collectées, de l'environnement.
Une fois que vous commenciez avec des pointeurs, le GC n'a aucune idée de ce qu'il se passe.

Plus précisément, il est impossible de dire si les objets sont accessibles, car il ne sait pas où votre pointeurs sont.
Il ne peut également déplacer des objets dans la mémoire, parce que ce serait briser votre pointeurs.

Tout cela pourrait être résolu par GC-suivi des pointeurs; c'est ce que les références sont.

Vous devez uniquement utiliser les pointeurs dans le désordre avancée de l'interopérabilité des scénarios ou hautement sophistiqués d'optimisation.
Si vous vous posez, vous ne devriez probablement pas.

5voto

James King Points 3620

Le GC peut déplacer des références autour; l'utilisation non sécuritaire tient un objet à l'extérieur de la table de contrôle, et permet d'éviter cela. "Fixe" les broches d'un objet, mais laisse le GC gérer la mémoire.

Par définition, si vous avez un pointeur vers l'adresse d'un objet, et la GC qui bouge, le pointeur de la souris n'est plus valide.

Pourquoi vous avez besoin de pointeurs: raison Principale est de travailler avec Dll non gérées, par exemple, ceux écrits en C++

A noter aussi, lorsque vous épinglez des variables et utiliser les pointeurs, vous êtes plus sensible à la fragmentation du segment.


Modifier

Vous avez abordé la question au cœur de tablespaces gérés de code non managé... comment la mémoire se libère?

Vous pouvez mélanger du code de la performance que vous décrivez, vous ne pouvez pas traverser réussi/non géré limites avec des pointeurs (c'est à dire que vous ne pouvez pas utiliser les pointeurs à l'extérieur de la "dangereux" contexte).

Comme pour la façon dont ils sont nettoyées... à Vous de gérer votre propre mémoire; les objets que votre pointeurs point à été créé/alloué (en général, dans la DLL C++) à l'aide de (espérons-le) CoTaskMemAlloc(), et vous devez libérer de la mémoire, de la même manière, en appelant CoTaskMemFree(), ou vous allez avoir une fuite de mémoire. Notez que seule la mémoire allouée avec CoTaskMemAlloc() peut être libéré avec CoTaskMemFree().

L'autre alternative est d'exposer une méthode à partir de votre natif C++ dll qui prend un pointeur et la libère... ce qui permet à la DLL décider comment libérer de la mémoire, ce qui fonctionne le mieux si elle a utilisé une autre méthode pour allouer de la mémoire. La plupart des dll natives vous travaillez avec dll de tiers que vous ne pouvez pas modifier, et ils n'ont généralement pas (que j'ai vu) de telles fonctions à appeler.

Un exemple de libérer de la mémoire, prises à partir d' ici:

string[] array = new string[2];
array[0] = "hello";
array[1] = "world";
IntPtr ptr = test(array);
string result = Marshal.PtrToStringAuto(ptr);
Marshal.FreeCoTaskMem(ptr);
System.Console.WriteLine(result);


Certains plus du matériel de lecture:

C# désalloue la mémoire référencée par IntPtr La deuxième réponse en bas de explique la différence d'allocation/désallocation de méthodes

Comment gratuit IntPtr en C#? Renforce la nécessité de libérer de la même manière, la mémoire a été allouée

http://msdn.microsoft.com/en-us/library/aa366533%28VS.85%29.aspx Officiel de la documentation MSDN sur les différentes manières d'allouer et de libérer de la mémoire.

Bref... vous avez besoin de savoir comment la mémoire a été allouée pour la libérer.


Modifier Si je comprends votre question correctement, la réponse courte est oui, vous pouvez à la main les données non gérées pointeurs, travailler avec elle dans un contexte dangereux, et les données disponibles une fois que vous quittez le contexte dangereux.

La clé est que vous avez à la broche de l'objet géré, vous faites référence à un fixed bloc. Cela empêche la mémoire, vous faites référence à d'être déplacé par le GC alors que dans l' unsafe bloc. Il y a un certain nombre de subtilités en cause ici, par exemple, vous ne pouvez pas réaffecter un pointeur initialisé dans un bloc fixe... vous devriez lire sur dangereux et fixe des instructions si vous êtes vraiment à la gestion de votre propre code.

Tout ce que dit, les avantages de la gestion de vos propres objets et en utilisant des pointeurs de la manière que vous décrivez ne peut pas vous acheter autant d'une augmentation de la performance que vous pourriez le penser. Raisons de ne pas:

  1. C# est très optimisé et très rapide
  2. Votre pointeur de code est généré comme IL, qui doit être jitted (à quel point des optimisations supplémentaires entrent en jeu)
  3. Vous n'êtes pas en tournant le Garbage Collector hors... vous êtes juste de garder les objets que vous travaillez avec de la GC de sa juridiction. Ainsi, chaque 100ms, le GC encore interrompt votre code et exécute ses fonctions pour toutes les autres variables dans votre code géré.

HTH,
James

3voto

Ondrej Tucny Points 13127

Les raisons les plus courantes d'utiliser des pointeurs explicitement en C#:

  • faire de bas niveau de travail (comme la manipulation de la chaîne) qui est très sensible aux performances,
  • l'interfaçage avec les Api non gérées.

La raison pour laquelle la syntaxe associée avec des pointeurs a été supprimé à partir de C# (en fonction de mes connaissances et mon point de vue - Jon Skeet répondrait mieux B)) a été il s'est avéré être superflu dans la plupart des situations.

À partir de la langue de conception de point de vue, une fois que vous gérer de la mémoire par un garbage collector, vous devez introduire des contraintes sévères sur ce qui est et ce qui n'est pas possible de le faire avec des pointeurs. Par exemple, à l'aide d'un pointeur dans le milieu d'un objet peut causer de graves problèmes à la GC. Par conséquent, une fois que les restrictions sont en place, vous pouvez omettre le supplément de la syntaxe et à la fin avec "automatique" des références.

Aussi, l'ultra-bienveillant approche en C/C++ est une source fréquente d'erreurs. Pour la plupart des situations, où les micro-performance n'est pas grave du tout, c'est mieux de proposer des règles plus strictes et de contraindre le développeur en faveur de moins de bugs qui seraient très difficiles à découvrir. Ainsi, pour le commun des applications d'affaires de la soi-disant "géré" des environnements tels que .NET et Java sont mieux adaptés que les langues qui présument que de travailler contre le nu-metal machine.

2voto

variable Points 564

Dites que vous voulez communiquer entre 2 application à l'aide de l'IPC (mémoire partagée), alors vous pouvez regrouper les données en mémoire et de transmettre ces données pointeur à l'autre application via la messagerie de windows ou de quelque chose. Lors de la réception de l'application, vous pouvez récupérer les données de retour.

Utile également en cas de transfert de données .NET de l'héritage VB6 apps dans lequel vous devrez regrouper les données en mémoire, passer le pointeur de VB6 application à l'aide de win msging, utiliser VB6 copymemory (pour récupérer des données à partir de la gestion de l'espace mémoire pour VB6 applications non gérées espace de mémoire..

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X