son - Cuándo usar punteros en C#/. NET?
punteros programacion (5)
¿Cuándo se necesita esto? ¿Bajo qué circunstancias el uso de punteros se vuelve inevitable?
Cuando el costo neto de una solución administrada y segura es inaceptable, pero el costo neto de una solución insegura es aceptable. Puede determinar el costo neto o beneficio neto al restar los beneficios totales de los costos totales. Los beneficios de una solución insegura son cosas como "no se pierde tiempo en verificaciones de tiempo de ejecución innecesarias para garantizar la corrección"; los costos son (1) tener que escribir un código que sea seguro incluso con el sistema de seguridad administrado desactivado, y (2) tener que lidiar con la posibilidad de que el recolector de basura sea menos eficiente, porque no puede mover la memoria que tiene un puntero no administrado eso.
O bien, si usted es la persona que escribe la capa de clasificación.
¿Es solo por motivos de rendimiento?
Parece perverso usar punteros en un lenguaje administrado por razones distintas al rendimiento.
Puede usar los métodos en la clase Marshal para tratar la interoperación con código no administrado en la gran mayoría de los casos. (Puede haber algunos casos en los que sea difícil o imposible usar el equipo de clasificación para resolver un problema de interoperabilidad, pero no conozco ninguno).
Por supuesto, como dije, si usted es la persona que escribe la clase Marshal, obviamente no podrá utilizar la capa de clasificación para resolver su problema. En ese caso, necesitaría implementarlo usando punteros.
¿Por qué C # expone esta funcionalidad a través de un contexto inseguro y elimina todas las ventajas administradas de ella?
Esas ventajas administradas vienen con costos de rendimiento. Por ejemplo, cada vez que solicite un décimo elemento en una matriz, el tiempo de ejecución debe verificar si hay un décimo elemento y lanzar una excepción si no lo hay. Con punteros, se elimina el costo del tiempo de ejecución.
El costo del desarrollador correspondiente es que si lo haces mal, tendrás que lidiar con errores de corrupción de memoria que formatean tu disco duro y bloquean tu proceso una hora más tarde en lugar de lidiar con una buena excepción limpia en el punto del error.
¿Es posible utilizar punteros sin perder ninguna ventaja del entorno gestionado, teóricamente?
Por "ventajas", supongo que se refiere a ventajas como recolección de basura, tipo de seguridad e integridad referencial. Por lo tanto, su pregunta es esencialmente "¿es posible en teoría apagar el sistema de seguridad pero aún así obtener los beneficios del sistema de seguridad encendido?" 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 que esté encendido!
Sé que C # le da al programador la posibilidad de acceder, usar punteros en un contexto inseguro. Pero, ¿cuándo se necesita esto?
¿En qué circunstancias, usar punteros se vuelve inevitable?
¿Es solo por motivos de rendimiento?
Además, ¿por qué C # expone esta funcionalidad a través de un contexto inseguro y elimina todas las ventajas administradas de ella? ¿Es posible tener punteros de uso sin perder ninguna ventaja del entorno gestionado, teóricamente?
Digamos que desea comunicarse entre 2 aplicaciones utilizando IPC (memoria compartida), luego puede ordenar los datos en la memoria y pasar este puntero a la otra aplicación a través de mensajes de Windows o algo así. Al recibir la aplicación puede recuperar datos.
También es útil en caso de transferencia de datos de .NET a aplicaciones VB6 heredadas en las que combinará los datos en la memoria, pasará el puntero a la aplicación VB6 usando win msging, usará VB6 copymemory () para recuperar datos del espacio de memoria administrado a las aplicaciones VB6 memoria no administrada espacio..
El GC puede mover referencias alrededor; usar inseguro mantiene un objeto fuera del control del GC, y lo evita. "Fijo" pines un objeto, pero deja 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: El motivo principal es trabajar con DLL no administradas, por ejemplo, los escritos en C ++.
También tenga en cuenta que cuando identifica variables y utiliza punteros, es más susceptible a la fragmentación del montón.
Editar
Has tocado el tema central del código administrado frente al no administrado ... ¿cómo se libera la memoria?
Puede mezclar código para el rendimiento tal como lo describe, simplemente no puede cruzar los límites gestionados / no gestionados con punteros (es decir, no puede usar punteros fuera del contexto "inseguro").
En cuanto a cómo se limpian ... Tienes que administrar tu propia memoria; los objetos a los que apuntan sus punteros fueron creados / asignados (generalmente dentro de la DLL de C ++) usando (con suerte) CoTaskMemAlloc()
, y debe liberar esa memoria de la misma manera, llamando a CoTaskMemFree()
, o tendrá una pérdida 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 de C ++ nativo que toma un puntero y lo libera ... esto permite que el DLL decida cómo liberar la memoria, que funciona mejor si utilizó algún otro método para asignar memoria. La mayoría de las dlls nativas con las que trabajas son dlls de terceros que no puedes modificar, y generalmente no tienen (que he visto) funciones para llamar.
Un ejemplo de liberación de memoria, tomado de here :
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 a la que hace referencia IntPtr La segunda respuesta 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 en que se asignó la memoria
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 correctamente su pregunta, la respuesta breve es sí, puede entregar los datos a punteros no administrados, trabajar con ellos en un contexto inseguro y tener los datos disponibles una vez que salga del contexto inseguro.
La clave es que debe fijar el objeto gestionado al que hace 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
. Aquí hay una serie de sutilezas, por ejemplo, no puede reasignar un puntero inicializado en un bloque fijo ... debe leer las declaraciones inseguras y fijas si realmente está configurado para administrar su propio código.
Dicho todo esto, los beneficios de administrar sus propios objetos y utilizar punteros de la manera que usted describe pueden no significarle un incremento en el rendimiento tan grande como podría pensar. Razones por las que no:
- C # está muy optimizado y es muy rápido
- El código del puntero se sigue generando como IL, que se debe jit (en este punto entran en juego más optimizaciones)
- No estás desactivando el recolector de basura ... solo mantienes los objetos con los que trabajas fuera del ámbito del GC. Así que cada 100 ms más o menos, el GC aún interrumpe su código y ejecuta sus funciones para todas las demás variables en su código administrado.
HTH,
James
Las razones más comunes para usar punteros explícitamente en C #:
- haciendo trabajos de bajo nivel (como la manipulación de cadenas) que es muy sensible al rendimiento,
- interactuando con API no administradas.
La razón por la cual la sintaxis asociada con los punteros se eliminó de C # (según mi conocimiento y punto de vista - Jon Skeet respondería mejor a B-)) resultó ser superflua en la mayoría de las situaciones.
Desde la perspectiva del diseño del lenguaje, una vez que administra la memoria de un recolector de basura, debe introducir restricciones severas sobre lo que es y lo que no es posible hacer con los punteros. Por ejemplo, usar un puntero para apuntar al 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 encontrado 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 aplicaciones comerciales comunes, los entornos llamados "administrados" como .NET y Java son más adecuados que los lenguajes que presumen trabajar en contra de la máquina bare-metal.
Los indicadores son una contradicción inherente al entorno gestionado, recogido de basura.
Una vez que empiezas a jugar con punteros crudos, el GC no tiene idea de lo que está pasando.
Específicamente, no puede decir si los objetos son alcanzables, ya que no sabe dónde están los punteros.
Tampoco puede mover objetos en la memoria, ya que eso rompería sus punteros.
Todo esto se resolvería con punteros seguidos por GC; eso es lo que son las referencias.
Solo debe usar punteros en escenarios desordenados de interoperabilidad avanzada o para una optimización altamente sofisticada.
Si tiene que preguntar, probablemente no debería.