¿Cuándo usar punteros en C#/. NET?


Sé que C# le da al programador la capacidad de acceder, usar punteros en un contexto inseguro. Pero, ¿cuándo es necesario?

¿En qué circunstancias, el uso de punteros se vuelve inevitable?

¿Es solo por razones de rendimiento?

También ¿por qué C# expone esta funcionalidad a través de un contexto inseguro y elimina todas las ventajas administradas de él? ¿Es posible tener punteros de uso sin perder ninguna ventaja del entorno gestionado, teóricamente?

Author: Joan Venge, 2011-03-02

5 answers

¿Cuándo se necesita esto? ¿Bajo qué circunstancias el uso de punteros se vuelve inevitable?

Cuando el costo neto de una solución segura y administrada es inaceptable, pero el costo neto de una solución insegura es aceptable. Puede determinar el costo neto o el beneficio neto restando los beneficios totales de los costos totales. Los beneficios de una solución insegura son cosas como "no perder tiempo en comprobaciones de tiempo de ejecución innecesarias para garantizar la corrección"; los costos son (1) tener que escribir código eso es seguro incluso con el sistema de seguridad administrado desactivado, y (2) tener que lidiar con potencialmente hacer que el recolector de basura sea menos eficiente, porque no puede moverse por la memoria que tiene un puntero no administrado en ella.

O, si usted es la persona que escribe la capa de marshalling.

¿Es solo por razones de rendimiento?

Parece perverso usar punteros en un lenguaje administrado por razones distintas al rendimiento.

Puede utilizar los métodos en el Clase Marshal para hacer frente a la interoperabilidad con código no administrado en la gran mayoría de los casos. (Puede haber algunos casos en los que es difícil o imposible usar el equipo de marshalling para resolver un problema de interop, pero no conozco ninguno.)

Por supuesto, como he dicho, si eres la persona que escribe la clase Marshal entonces obviamente no tienes que usar la capa marshalling para resolver tu problema. En ese caso, tendría que implementarlo usando punteros.

Por qué ¿C# expone esta funcionalidad a través de un contexto inseguro y elimina todas las ventajas administradas?

Esas ventajas administradas vienen con costos de rendimiento. Por ejemplo, cada vez que se le pide a un array su décimo elemento, el tiempo de ejecución necesita hacer una comprobación para ver si hay un décimo elemento, y lanzar una excepción si no lo hay.

El costo correspondiente del desarrollador es que si lo haces mal, entonces tienes que lidiar con errores de corrupción de memoria que formatea su disco duro y bloquea su proceso una hora más tarde en lugar de tratar con una excepción limpia y agradable en el punto del error.

¿Es posible usar punteros sin perder ninguna ventaja del entorno administrado, teóricamente?

Por "ventajas" asumo que te refieres a ventajas como la recolección de basura, la seguridad de tipos y la integridad referencial. Por lo tanto, su pregunta es esencialmente " ¿es en teoría posible apagar la seguridad sistema, pero todavía obtener los beneficios del sistema de seguridad que se enciende?"No, claramente no lo es. Si apaga ese sistema de seguridad porque no le gusta lo caro que es, ¡entonces no obtiene los beneficios de estar encendido!

 78
Author: Eric Lippert,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-02 19:36:29

Los punteros son una contradicción inherente al entorno gestionado y recolectado de basura.
Una vez que empiezas a jugar con punteros crudos, el GC no tiene ni idea de lo que está pasando.

Específicamente, no puede decir si los objetos son accesibles, ya que no sabe dónde están sus punteros.
Tampoco puede mover objetos en la memoria, ya que eso rompería sus punteros.

Todo esto se resolvería mediante punteros de seguimiento GC; eso es lo que son las referencias.

Usted debe solo use punteros en escenarios de interop avanzados desordenados o para una optimización altamente sofisticada.
Si tienes que preguntar, probablemente no deberías.

 15
Author: SLaks,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-02 19:01:52

El GC puede mover referencias; usando unsafe mantiene un objeto fuera del control del GC, y evita esto. "Fixed" fija un objeto, pero permite que el GC administre la memoria.

Por definición, si tiene un puntero a la dirección de un objeto, y el GC lo mueve, su puntero ya no es válido.

En cuanto a por qué necesita punteros: La razón principal es trabajar con DLL no administrados, por ejemplo, aquellos escritos en C++

También tenga en cuenta que cuando fija variables y usa punteros, es más susceptible a la fragmentación del montón.


Editar

Ha tocado el problema principal de código administrado vs.no administrado... ¿cómo se libera la memoria?

Puede mezclar código para el rendimiento como describe, simplemente no puede cruzar los límites administrados/no administrados con punteros (es decir, no puede usar punteros fuera del contexto 'inseguro').

En cuanto a cómo se limpian... Tienes que gestionar tu propia memoria; los objetos a los que apuntan tus punteros eran creado / asignado (generalmente dentro de la DLL de C++) usando (con suerte) CoTaskMemAlloc(), y tienes que liberar esa memoria de la misma manera, llamando a CoTaskMemFree(), o tendrás una fuga de memoria. Tenga en cuenta que solo la memoria asignada con CoTaskMemAlloc() se puede liberar con CoTaskMemFree().

La otra alternativa es exponer un método de su dll nativo de C++ que toma un puntero y lo libera... esto le permite al DLL decidir cómo liberar la memoria, que funciona mejor si utiliza algún otro método para asignar memoria. La mayoría de los dlls nativos que trabajar con son dlls de terceros que no se puede modificar, y no suelen tener (que he visto) tales funciones para llamar.

Un ejemplo de liberar memoria, tomado de aquí:

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);


Un poco más de material de lectura:

C # desasignar memoria referenciada por IntPtr La segunda respuesta abajo explica los diferentes métodos de asignación/desasignación

Cómo liberar IntPtr en C#? Refuerza la necesidad de desasignar de la misma manera la la memoria fue asignada

Http://msdn.microsoft.com/en-us/library/aa366533%28VS.85%29.aspx Documentación oficial de MSDN sobre las diversas formas de asignar y desasignar memoria.

En resumen... necesita saber cómo se asignó la memoria para liberarla.


Editar Si entiendo su pregunta correctamente, la respuesta corta es sí, puede entregar los datos a punteros no administrados, trabajar con ellos en un contexto inseguro y tener los datos disponibles una vez sales del contexto inseguro.

La clave es que tienes que anclar el objeto administrado al que estás haciendo referencia con un bloque fixed. Esto evita que la memoria a la que hace referencia sea movida por el GC mientras está en el bloque unsafe. Hay una serie de sutilezas involucradas aquí, por ejemplo, no se puede reasignar un puntero inicializado en un bloque fijo... deberías leer las instrucciones inseguras y fijas si realmente estás listo para administrar tu propio código.

Todo lo dicho, los beneficios de la gestión de su los objetos propios y el uso de punteros de la manera que describe puede no comprar tanto de un aumento de rendimiento como se podría pensar. Razones por las que no:

  1. C # es muy optimizado y muy rápido
  2. Su código de puntero todavía se genera como IL, que tiene que ser jitted (momento en el que entran en juego otras optimizaciones)
  3. No estás apagando el Recolector de Basura... sólo mantienes los objetos con los que trabajas fuera del ámbito de la GC. Así que cada aproximadamente 100ms, el GC todavía interrumpe su código y ejecuta sus funciones para todas las demás variables en su código administrado.

HTH,
James

 4
Author: James King,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:25:50

Las razones más comunes para usar punteros explícitamente en C#:

  • haciendo trabajo de bajo nivel (como la manipulación de cadenas) que es muy sensible al rendimiento,
  • interfaz con API no administradas.

La razón por la que la sintaxis asociada con los punteros se eliminó de C# (según mi conocimiento y punto de vista - Jon Skeet respondería mejor B-)) fue que resultó ser superflua en la mayoría de las situaciones.

Desde la perspectiva del diseño del lenguaje, una vez que gestione memoria por un recolector de basura tienes que introducir restricciones severas sobre lo que es y lo que no es posible hacer con los punteros. Por ejemplo, usar un puntero para apuntar hacia el centro de un objeto puede causar problemas graves al GC. Por lo tanto, una vez que las restricciones están en su lugar, puede omitir la sintaxis adicional y terminar con referencias "automáticas".

Además, el enfoque ultra benevolente que se encuentra en C/C++ es una fuente común de errores. Para la mayoría de las situaciones, donde el micro-rendimiento no importa en absoluto, es mejor ofrecer reglas más estrictas y restringir al desarrollador a favor de menos errores que serían muy difíciles de descubrir. Por lo tanto, para las aplicaciones comerciales comunes, los llamados entornos "administrados" como.NET y Java son más adecuados que los lenguajes que presumen trabajar contra la máquina de metal desnudo.

 2
Author: Ondrej Tucny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-03-02 18:50:41

Digamos que desea comunicarse entre 2 aplicaciones utilizando IPC (memoria compartida), entonces puede ordenar los datos a la memoria y pasar este puntero de datos a la otra aplicación a través de Windows messaging o algo así. Al recibir la aplicación puede recuperar los datos.

Útil también en caso de transferir datos desde. NET a aplicaciones VB6 heredadas en las que se marshal los datos a la memoria, pasar puntero a la aplicación VB6 utilizando win msging, utilizar VB6 copymemory () para obtener datos desde el espacio de memoria administrado a VB6 apps espacio de memoria no administrado..

 1
Author: variable,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-28 04:38:51