Quand utiliser les pointeurs dans 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, utiliser des pointeurs devient inévitable?

est-ce seulement pour des raisons de performance?

pourquoi c# expose-t-il cette fonctionnalité à travers un contexte dangereux, et en supprime-t-il tous les avantages managés? Est-il possible d'utiliser des pointeurs sans perdre les avantages de environnement géré, en théorie?

59
demandé sur Joan Venge 2011-03-02 21:32:31

5 réponses

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

lorsque le coût net d'une solution sûre gérée est inacceptable, mais que le coût net d'une solution non sécuritaire est acceptable. Vous pouvez déterminer le coût net ou l'avantage net en soustrayant le total des avantages du total des coûts. Les avantages d'une solution non sécuritaire sont des choses comme " pas de temps perdu sur les contrôles d'exécution inutiles pour assurer l'exactitude"; les coûts sont (1) devoir écrire un code qui est sûr même avec le système de sécurité géré éteint, et (2) devoir faire face à potentiellement rendre le collecteur d'ordures moins efficace, parce qu'il ne peut pas se déplacer autour de la mémoire qui a un pointeur non géré dans elle.

ou, si vous êtes la personne qui écrit la couche marshalling.

est-ce seulement pour des raisons de performance?

Il semble pervers d'utiliser des pointeurs dans une langue gérée pour des raisons autres que la performance.

vous pouvez utiliser les méthodes de la classe Marshal pour traiter de l'interopérabilité avec le code non géré dans la grande majorité des cas. (Il peut y avoir quelques cas où il est difficile ou impossible d'utiliser l'équipement de formation pour résoudre un problème interop, mais je n'en connais aucun.)

bien sûr, comme je l'ai dit, si vous êtes la personne qui écrit le cours de Marshal alors évidemment vous ne pouvez pas utilisez la couche de formation pour résoudre votre problème. Dans ce cas, vous aurez besoin de l'implémenter en utilisant des pointeurs.

pourquoi c# expose-t-il cette fonctionnalité dans un contexte dangereux, et en supprime-t-il tous les avantages gérés?

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

le coût correspondant du développeur est que si vous le faites mal, alors vous avez à faire face à des bogues de corruption de mémoire qui formate votre disque dur et bloque votre processus une heure plus tard plutôt que de faire face à une belle exception propre au point de l'erreur.

est-il possible d'utiliser des pointeurs sans perdre aucun avantage de managé environnement, théoriquement?

par" avantages", je suppose que vous voulez dire des avantages comme la collecte des ordures, la sécurité de type et 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 ce système de sécurité parce que vous n'aimez pas à quel point il est cher, alors vous n'obtenez pas les avantages d'être sur!

78
répondu Eric Lippert 2011-03-02 19:36:29

les pointeurs sont une contradiction inhérente à l'environnement géré et collecté des ordures.

Une fois que vous commencez à jouer avec des pointeurs bruts, le GC n'a aucune idée de ce qui se passe.

spécifiquement, il ne peut pas dire si les objets sont accessibles, car il ne sait pas où vos pointeurs sont.

Il ne peut pas non plus déplacer des objets en mémoire, car cela briserait vos pointeurs.

Tout cela serait résolu par des pointeurs GC-tracés; voilà ce que sont les références.

vous ne devez utiliser des pointeurs que dans des scénarios Interop avancés ou pour une optimisation très sophistiquée.

Si tu dois demander, tu ne devrais probablement pas.

15
répondu SLaks 2011-03-02 19:01:52

le GC peut déplacer des références; l'utilisation de danger permet de garder un objet hors du contrôle du GC et d'éviter cela. "Fixe" épingle 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 que le GC le déplace, votre pointeur n'est plus valide.

quant à la raison pour laquelle vous avez besoin de pointeurs: la raison principale est de travailler avec des DLLs non gérées, p.ex. celles écrites en C++

Note complémentaire, quand vous épinglez des variables et utilisez des pointeurs, vous êtes plus susceptible à la fragmentation tas.



modifier

vous avez abordé la question centrale du code géré par opposition au code non géré... comment la mémoire se libère?

vous pouvez mélanger le code pour la performance comme vous le décrivez, vous ne pouvez pas traverser les limites gérées/non gérées avec des pointeurs (i.e. vous ne pouvez pas utiliser des pointeurs en dehors de la 'dangereuse' cadre.)

pour le nettoyage... Vous devez gérer votre propre mémoire; les objets vers lesquels vos pointeurs pointent ont été créés/alloués (généralement dans la DLL C++) en utilisant (avec un peu de chance) CoTaskMemAlloc() , et vous devez libérer cette mémoire de la même manière, en appelant CoTaskMemFree() , ou vous aurez une fuite de mémoire. Notez que seule la mémoire attribuée avec CoTaskMemAlloc() peut être libérée avec CoTaskMemFree() .

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

exemple de mémoire libératrice, tiré de 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);



Quelque plus de matériel de lecture:

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

Comment libérer IntPtr en C#? Renforce la nécessité de délocaliser de la même manière que la mémoire a été attribuée

http://msdn.microsoft.com/en-us/library/aa366533%28VS.85%29.aspx MSDN officiel documentation sur les différentes façons d'allouer et de désallouer la mémoire.

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



modifier Si je comprends bien votre question, la réponse courte est oui, vous pouvez transférer les données à des indicateurs non gérés, travailler avec elle dans un contexte dangereux, et avoir les données disponibles une fois que vous quittez le contexte dangereux.

la clé est que vous devez épingler l'objet géré auquel vous faites référence avec un bloc fixed . Cela empêche la mémoire que vous référencez d'être déplacée par le GC dans le bloc unsafe . Il y a un certain nombre de subtilités impliquées ici, par exemple vous ne pouvez pas réattribuer un pointeur initialisé dans un bloc fixe... vous devriez lire sur les déclarations dangereuses et fixes Si vous êtes vraiment fixé sur la gestion de votre propre code.

tout cela dit, les avantages de la gestion vos propres objets et l'utilisation de pointeurs de la manière que vous décrivez peuvent ne pas vous acheter autant d'une augmentation de la performance que vous pourriez penser. Raisons pourquoi pas:

  1. C# est très optimisé et très rapide

  2. votre code de pointeur est encore généré comme IL, ce qui doit être modifié (à ce moment-là d'autres optimisations entrent en jeu)

  3. tu ne retournes pas les ordures Collecteur off... vous gardez juste les objets avec lesquels vous travaillez hors du champ de compétence du GC. 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

4
répondu James King 2017-05-23 12:25:50

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

  • faire un travail de bas niveau (comme la manipulation de cordes) 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 aux pointeurs a été retirée de C# (selon ma connaissance et point de vue - Jon Skeet répondrait mieux B-)) s'est avérée être superflue dans la plupart des situations.

du point de vue de la conception du langage, une fois que vous gérez la mémoire par un collecteur d'ordures, vous devez introduire de sévères contraintes sur ce qui est et ce qui n'est pas possible de faire avec des pointeurs. Par exemple, l'utilisation d'un pointeur pour pointer vers le milieu d'un objet peut causer de graves problèmes au GC. Par conséquent, une fois que les restrictions sont en place, vous pouvez simplement omettre la syntaxe supplémentaire et finir avec des références "automatiques".

aussi, l'approche ultra-bienveillante trouvé dans c / c++ est une source commune d'erreurs. Pour la plupart des situations, où la micro-performance n'a pas d'importance du tout, il est préférable d'offrir des règles plus strictes et de contraindre le développeur en faveur de moins de bogues qui seraient très difficiles à découvrir. Ainsi, pour les applications professionnelles courantes, les environnements dits" managés " comme .NET et Java conviennent mieux que les langages qui prétendent fonctionner contre la machine à métal nu.

2
répondu Ondrej Tucny 2011-03-02 18:50:41

dites que vous voulez communiquer entre 2 applications en utilisant IPC (shared memory) alors vous pouvez rassembler les données en mémoire et passer ce pointeur de données à l'autre application via Windows messaging ou quelque chose. À la réception de l'application, vous pouvez récupérer des données.

également utile en cas de transfert de données à partir de .net vers les applications VB6 traditionnelles où vous allez rassembler les données en mémoire, passer pointeur vers l'application VB6 en utilisant win msging, utiliser VB6 copymemory () pour récupérer les données à partir de la espace mémoire géré pour applications VB6 espace mémoire non géré..

1
répondu variable 2014-01-28 04:38:51