when pattern net implement example correctly asp .net dispose idisposable

.net - pattern - ¿Cómo manejar la excepción lanzada desde Dispose?



using c# (7)

Debido a que no tiene que asignar las variables en una declaración using (), ¿por qué no usar declaraciones apiladas para esto?

void Dispose() { // the example in the question didn''t use the full protected Dispose(bool) pattern // but most code should have if (!disposed) { if (disposing) { ... using (m_foo) using (m_bar) { // no work, using statements will check null // and call Dispose() on each object } m_bar = null; m_foo = null; }

Las declaraciones de uso ''apiladas'' se expanden así:

using (m_foo) { using (m_bar) { /* do nothing but call Dispose */ } }

Así que las llamadas Dispose () se ponen en bloques separados finalmente:

try { try { // do nothing but call Dispose } finally { if (m_bar != null) m_bar.Dispose(); } finally { if (m_foo != null) m_foo.Dispose(); }

Me costó encontrar una referencia para esto en un solo lugar. Las declaraciones de uso "apiladas" se encuentran en una antigua publicación del blog de Joe Duffy (consulte la sección ''Declaración de uso de C # y VB, Semántica de pila de C ++''). La publicación de Joe Duffy es referenciada por muchas respuestas de StackOverflow en IDisposable. También encontré una pregunta reciente en la que las declaraciones de uso apiladas para variables locales parecen ser comunes. No pude encontrar el encadenamiento de los bloques finalmente en ninguna otra parte que no sea la especificación del lenguaje C # (sección 8.13 en la especificación C # 3.0), y solo para múltiples variables dentro de un solo bloque ''utilizando'', que no es exactamente lo que estoy proponiendo, pero Si desarma el IL, encontrará que los bloques try / finally están anidados. En la comprobación de nulos, también de la especificación de C #: ''Si se adquiere un recurso nulo, no se realiza ninguna llamada a Dispose y no se lanza ninguna excepción.''

Recientemente, estuve investigando algunos errores complicados sobre objetos no eliminados.

Encontré algún patrón en el código. Se informa que algunos m_foo no se eliminan, mientras que parece que todas las instancias de SomeClass se han eliminado.

public class SomeClass: IDisposable { void Dispose() { if (m_foo != null) { m_foo.Dispose(); } if (m_bar != null) { m_bar.Dispose(); } } private Foo m_foo; private Bar m_bar; }

Sospecho que Foo.Dispose podría lanzar una excepción, por lo que el siguiente código no se ejecuta, por lo que m_bar no se elimina.

Dado que Foo / Bar puede ser de un tercero, no se garantiza que no se lance la excepción.

Si simplemente envuelve toda la invocación de Dispose con try-catch, el código se volverá torpe.

¿Cuál es la mejor práctica para manejar esto?



En mi caso, se debió a un subproceso que accedía al elemento UI al cerrar el formulario. Lo resolví abortando el hilo en forma cerrada. (Evento "FormClosing")

FormClosing += (o, e) => worker.Abort();


Es cierto que puede ser bastante malo filtrar una excepción de su método de disposición, especialmente porque las cosas que implementan IDisposable generalmente especificarán un finalizador que llamará a Dispose.

El problema es que barrer el problema debajo de la alfombra manejando una excepción puede dejarlo en algunas situaciones muy difíciles de depurar . ¿Qué pasa si su IDisponible asignó una sección crítica de clases que solo se libera después de desecharse? Si ignora el hecho de que ocurrió la excepción, puede terminar en un punto muerto central. Creo que las fallas en Dispose deberían ser uno de esos casos en los que querrás fallar antes, para que puedas corregir el error tan pronto como se descubra.

Por supuesto, todo depende del objeto que se deseche, para algunos objetos que puede recuperar y otros no. Como regla general, Dispose no debe lanzar excepciones cuando se usa correctamente y no debe tener que codificar defensivamente alrededor de las excepciones en los métodos de Dispose anidados que está llamando.

¿Realmente no quieres barrer una excepción OutOfMemoryException debajo de la alfombra?

Si tuviera un componente de terceros poco fiable que arrojara excepciones de forma arbitraria en Dispose, lo arreglaría y lo alojaría en un proceso separado que podría eliminar cuando comenzó a reproducirse.


Para evitar repetir el código para desechar objetos, escribí el siguiente método estático.

public static void DisposeObject<T>(ref T objectToDispose) where T : class { IDisposable disposable = objectToDispose as IDisposable; if (disposable == null) return; disposable.Dispose(); objectToDispose = null; }

El punto principal es que puede convertirla en una función, por lo que solo escribe una línea por cada objeto que desea desechar, manteniendo los métodos de desechar ordenados y limpios. En nuestro caso, era una convención anular los punteros eliminados, de ahí los parámetros de referencia.

En su caso, es posible que desee agregar un manejo de excepciones, o hacer un sabor diferente con el manejo de excepciones. Me aseguraría de que registre / corte el punto de referencia cada vez que Dispose () arroje excepciones, pero si no puede evitarlo, lo siguiente es asegurarse de que el problema no se propague.


Según las reglas de diseño :

"Un método IDisposable.Dispose no debe lanzar una excepción".

Entonces, si su programa se bloquea debido a una excepción no controlada de Dispose (), consulte la Solución oficial


Si se llama a Dispose () dentro de un contexto de finalización y se produce una excepción, su proceso finalizará.

Si sospecha que Foo.Dispose () está lanzando una excepción, la desecharía al final, si es posible, y la envolvería en try / catch. Haga todo lo que pueda para deshacerse de él en la captura: establezca la referencia en nulo. Es muy malo lanzar excepciones desde Dispose () y debe evitarse.

Desafortunadamente, si este es un código de terceros con errores, lo mejor sería conseguir que lo corrijan. No deberías tener que limpiar manualmente después de eso.

Espero que ayude.