stayin miembros gibb gees bee barry alive .net struct destructor raii value-type

.net - gibb - bee gees miembros



¿Por qué no hay RAII en.NET? (7)

Brian Harry tiene una buena publicación sobre los fundamentos here .

Aquí hay un extracto:

¿Qué hay de la finalización determinística y los tipos de valores (estructuras)?

-------------- He visto muchas preguntas sobre estructuras que tienen destructores, etc. Esto vale la pena comentar. Hay una variedad de problemas por los que algunos idiomas no los tienen.

(1) composición: en el caso general, no le dan una vida determinista para los mismos tipos de razones de composición descritas anteriormente. Cualquier clase no determinista que contenga uno no llamaría al destructor hasta que fuera finalizado por el GC de todos modos.

(2) copiar constructores: el único lugar donde sería realmente bueno es en los locales asignados a la pila. Tendrían acceso al método y todo sería genial. Desafortunadamente, para que esto realmente funcione, también debe agregar constructores de copia y llamarlos cada vez que se copia una instancia. Esta es una de las cosas más feas y complejas sobre C ++. Terminas obteniendo código ejecutándose por todas partes donde no lo esperas. Causa racimos de problemas de lenguaje. Algunos diseñadores de idiomas han elegido mantenerse alejados de esto.

Digamos que creamos estructuras con destructores pero añadimos un montón de restricciones para hacer que su comportamiento sea más razonable en vista de los problemas anteriores. Las restricciones serían algo así como:

(1) Solo puede declararlos como variables locales.

(2) Solo puedes pasarlos por ref

(3) No puede asignarlos, solo puede acceder a campos y llamar a métodos sobre ellos.

(4) No puedes boxearlos.

(5) Problemas al usarlos a través de Reflection (enlace tardío) porque generalmente involucra el boxeo.

tal vez más, pero ese es un buen comienzo.

¿De qué servirían estas cosas? ¿Realmente crearía un archivo o una clase de conexión de base de datos que SÓLO se puede utilizar como una variable local? No creo que nadie realmente lo haría. Lo que haría en su lugar sería crear una conexión de propósito general y luego crear una envoltura autodestruida para utilizarla como una variable local de ámbito. La persona que llama luego elegiría lo que querían usar. Tenga en cuenta que la persona que llama tomó una decisión y no está completamente encapsulada en el objeto en sí. Dado que puede usar algo así como las sugerencias que aparecen en un par de secciones.

El reemplazo de RAII en .NET es el patrón de uso, que funciona casi tan bien una vez que te acostumbras.

Siendo principalmente un desarrollador de C ++, la ausencia de RAII (Adquisición de recursos es inicialización) en Java y .NET siempre me ha molestado. El hecho de que la carga de limpiar se mueve del escritor de la clase a su consumidor (por medio de try finally o .NET que using constructo ) parece ser marcadamente inferior.

Veo por qué en Java no hay soporte para RAII ya que todos los objetos están ubicados en el montón y el recolector de basura no admite inherentemente destrucción determinística, pero en .NET con la introducción de los tipos de valor ( struct ) tenemos el (aparentemente ) candidato perfecto para RAII. Un tipo de valor que se crea en la pila tiene un alcance bien definido y se puede usar la semántica del destructor de C ++. Sin embargo, el CLR no permite que un tipo de valor tenga un destructor.

Mis búsquedas aleatorias encontraron un argumento de que si un tipo de valor está encuadrado cae bajo la jurisdicción del recolector de basura y, por lo tanto, su destrucción se vuelve no determinista. Siento que este argumento no es lo suficientemente fuerte, los beneficios de RAII son lo suficientemente grandes como para decir que un tipo de valor con un destructor no se puede encasillar (o usar como un miembro de la clase).

Para abreviar, mi pregunta es : ¿hay otras razones por las que los tipos de valores no se pueden usar para introducir RAII en .NET? (¿O crees que mi argumento sobre las ventajas obvias de RAII es defectuoso?)

Edición: no debo haber formulado la pregunta con claridad ya que las primeras cuatro respuestas han omitido el punto. sobre Finalize y sus características no deterministas, sé sobre el using construcción y creo que estas dos opciones son inferiores a RAII. using es una cosa más que el consumidor de una clase debe recordar (¿cuántas personas olvidaron poner un StreamReader en un bloque de using ?). Mi pregunta es filosófica sobre el diseño del lenguaje, ¿por qué es así y cómo se puede mejorar?

Por ejemplo, con un tipo de valor destructible determinísticamente genérico, puedo hacer que las palabras clave de using y lock redundantes (alcanzables por las clases de la biblioteca):

public struct Disposer<T> where T : IDisposable { T val; public Disposer(T t) { val = t; } public T Value { get { return val; } } ~Disposer() // Currently illegal { if (val != default(T)) val.Dispose(); } }

No puedo evitar terminar con una cita a propósito que vi una vez pero que actualmente no puedo encontrar su origen.

Puedes tomar mi destrucción determinista cuando mi mano muerta fría se salga del alcance. - Anon


En mi humilde opinión, las grandes cosas que necesitan VB.net y C # son:

  1. una declaración de "uso" para los campos, lo que haría que el compilador genere código para eliminar todos los campos así etiquetados. El comportamiento predeterminado debe ser que el compilador haga que una clase implemente IDisposable si no lo hace, o inserte la lógica de eliminación antes del inicio de la rutina principal de eliminación para cualquiera de una serie de patrones comunes de implementación IDisposal, o bien use un atributo para especificar que el material de desecho debe ir en una rutina con un nombre particular.
  2. un medio de disposición determinista de objetos cuyos constructores y / o inicializadores de campo arrojen una excepción, ya sea por un comportamiento predeterminado (llamando al método de eliminación predeterminado) o un comportamiento personalizado (llamando a un método con un nombre particular).
  3. Para vb.net, un método autogenerado para anular todos los campos WithEvent.

Todos ellos pueden funcionar bastante bien en vb.net, y algo menos bien en C #, pero el soporte de primera clase para ellos mejoraría ambos idiomas.


Excelente pregunta y una que me ha molestado mucho. Parece que los beneficios de RAII se perciben de manera muy diferente. En mi experiencia con .NET, la falta de una colección de recursos determinista (o al menos confiable) es uno de los principales inconvenientes. De hecho, .NET me ha obligado varias veces a emplear arquitecturas completas para tratar con recursos no administrados que podrían (pero no) requerir una recopilación explícita. Lo cual, por supuesto, es un gran inconveniente porque hace que la arquitectura general sea más difícil y desvía la atención del cliente de los aspectos más centrales.


Hay algunos subprocesos similares si los busca, pero básicamente, lo que se reduce a esto es que si desea RAII en .NET simplemente implemente un tipo IDisposable y use la instrucción "using" para obtener una eliminación determinista. De esa manera, muchos de los mismos ideos pueden implementarse y usarse de una manera un poco más prolija.


Lo más cerca que puedes llegar a eso es el operador de stackalloc muy limitado.


Puede hacer una forma de RAII en .net y java utilizando los métodos de finalización (). Se invoca una sobrecarga de finalización () antes de que el GC limpie la clase y, por lo tanto, se puede utilizar para limpiar cualquier recurso que la clase no pueda conservar (mutexes, sockets, manejadores de archivos, etc.). Sin embargo, aún no es determinista.

Con .NET puedes hacer algo de esto de manera determinista con la interfaz IDisposable y la palabra clave using, pero esto tiene limitaciones (usando constructo cuando se usa para comportamiento determinista, todavía no hay desasignación de memoria determinística, no se usa automáticamente en las clases, etc.).

Y sí, creo que hay un lugar para introducir ideas de RAII en .NET y otros lenguajes administrados, aunque el mecanismo exacto podría debatirse interminablemente. La única alternativa que podría ver sería introducir un GC que pudiera manejar la limpieza arbitraria de recursos (no solo la memoria), pero luego tendría problemas cuando dichos recursos tengan que ser liberados de manera determinista.


Un título mejor sería "¿Por qué no hay RAII en C # / VB". C ++ / CLI (La evolución del aborto que se administró C ++) tiene RAII en el mismo sentido que C ++. Todo es solo azúcar de sintaxis para el mismo patrón de finalización que utilizan el resto de los lenguajes de la CLI (los Destructores en objetos gestionados para C ++ / CLI son efectivamente finalizadores), pero está ahí.

Puede ser que le guste http://blogs.msdn.com/hsutter/archive/2004/07/31/203137.aspx