2009-01-21 15 views
14

Esto es lo que yo entiendo de IDisposable y finalizadores de "CLR a través de C#", "A partir de C#" y otros recursos:Singleton con finalizador pero no IDisposable

  • IDisposable es para limpiar administrado y recursos no administrados de forma determinista.
  • Las clases que son responsables de recursos no administrados (por ejemplo, identificadores de archivos) deben implementar IDisposable y proporcionar un finalizador para garantizar que se limpien incluso si el código del cliente no llama a Dispose() en la instancia.
  • Las clases que son responsables de los recursos administrados solo nunca deben implementar un finalizador.
  • Si tiene un finalizador, debe implementar IDisposable (esto permite que el código del cliente haga lo correcto y llame a Dispose(), mientras que el finalizador evita la fuga de recursos si se olvidan).

Si bien entiendo el razonamiento para y estoy de acuerdo con todo lo anterior, no es un escenario en el que creo que tiene sentido para romper estas reglas: una clase Singleton que es responsable de los recursos no administrados (por ejemplo, proporcionando un único punto de acceso a archivos particulares).

Creo que siempre es incorrecto tener un método Dispose() en un singleton porque la instancia singleton debería vivir durante la vida de la aplicación y si algún código del cliente llama a Dispose(), entonces está lleno. Sin embargo, desea un finalizador para que cuando la aplicación se descargue, el finalizador pueda limpiar los recursos no administrados.

Así que tener una clase singleton con un finalizador que no implementa IDisposable me parece una opción razonable, pero este tipo de diseño es contrario a lo que entiendo son las mejores prácticas.

¿Es este un enfoque razonable? Si no, ¿por qué no y cuáles son las alternativas superiores?

Respuesta

3

Si el recurso no administrado se lanza solo al salir de la aplicación, ni siquiera tiene que preocuparse por un finalizador, ya que la descarga del proceso debería ocuparse de esto de todos modos.

Si tiene varios dominios de aplicación y desea lidiar con la descarga de un dominio de aplicación, ese es un problema posible, pero posiblemente uno que no necesita preocuparse.

En segundo lugar, aquellos que dicen que este diseño posiblemente no sea el correcto (y dificultarán la reparación, posteriormente verán que realmente necesitan dos instancias) Crear el objeto (o un objeto de envoltura de carga lenta)) en su punto de entrada y pasarlo por el código hasta donde sea necesario para dejar en claro quién es el responsable de proporcionarlo a quién, entonces usted es libre de cambiar la decisión de usar solo uno con poco efecto para el resto del código (que utiliza lo que se da)

4

Menciono primero que los patrones de diseño orientados a objetos y sus consecuencias no siempre influyen en cada decisión de lenguaje, incluso en lenguajes orientados a objetos. Sin duda, puede encontrar patrones de diseño clásicos que son más fáciles de implementar en un idioma (Smalltalk) en lugar de otro (C++).

Dicho esto, no estoy seguro de estar de acuerdo con la premisa de que una instancia de singleton solo debe eliminarse al final de una aplicación. Nada en las descripciones de patrones de diseño que he leído para Singleton (o Design Patterns: Elements of reusable Object-Oriented Software) menciono esto como una propiedad de este patrón. Un singleton debe garantizar que solo existe una instancia de la clase en cualquier momento en el tiempo; eso no implica que deba existir mientras exista la aplicación.

Tengo la sensación de que, en la práctica, existen muchos singletons durante la mayor parte de la vida de una aplicación. Sin embargo, considere una aplicación que utiliza una conexión TCP para comunicarse con un servidor, pero también puede existir en un modo desconectado. Cuando esté conectado, deseará que un singleton mantenga la información de conexión y el estado de la conexión. Una vez desconectado, es posible que desee conservar ese mismo singleton o puede deshacerse del singleton. Mientras que algunos pueden argumentar que tiene más sentido mantener el singleton (e incluso puedo estar entre ellos), no hay nada en el patrón de diseño que impida que usted se deshaga de él. Si se rehace una conexión, se puede instanciar el singleton. de nuevo, ya que no existe ninguna instancia de eso en ese momento.

En otras palabras, puede crear escenarios en los que es lógico que los singleton tengan IDisposable.

+1

El hecho es que, si puede concebir un motivo para que su objeto sea reemplazado durante la vida útil de la aplicación, el objeto no puede ser legítimamente un singleton. En cuanto a por qué ... Digamos que agarré el Singleton y lo pasé a las cosas que lo necesitaban (porque, ya sabes, la inyección de dependencia es algo bueno). Entonces algo en algún lugar decide reemplazarlo. Ahora hay * dos singletons * (el anterior que he estado pasando, y el nuevo). Esta misma posibilidad viola la definición completa de Singleton, es decir, ese código siempre ve exactamente una instancia. – cHao

+0

Entonces, antes que nada, santo viejo responde Batman. En segundo lugar, su ejemplo no parece tener sentido.Si pasa una instancia de Singleton a un objeto y luego otro decide que quiere el Singleton, entonces el patrón lo permite; debe obtener una referencia de regreso al objeto que se encuentra actualmente en la memoria. El punto que estaba haciendo era que el patrón no le impide deshacerse del objeto. En ese caso, usando su ejemplo, si el objeto fue eliminado, la instancia única ya no debería existir, por lo que cuando un objeto diferente solicita el Singleton, debería obtener una nueva instancia. –

+2

Si se elimina el objeto, la instancia * does * aún existe; toda persona que la haya adquirido antes de que se haya eliminado aún la tendrá. Por lo tanto, obviamente no se puede convertir en GC, pero ahora no se puede usar. Piensa en las consecuencias de esa afirmación en un contexto multiproceso. Una expresión como 'Singleton.getInstance(). DoStuff()' ya no es segura para hilos, * incluso si tanto 'getInstance' como' doStuff' son *. Podría aparecer un hilo entre las dos llamadas y eliminar la instancia que acabamos de adquirir. Y eso incluso está asumiendo su escenario mítico de todo el mundo-siempre-llama-'getInstance'. – cHao

3

Siempre que su finalizador no llame a métodos (como Dispose) en ningún otro objeto administrado, debería estar bien. Solo recuerde que el orden de finalización no es determinista. Es decir, si su objeto singleton Foo mantiene una referencia al objeto de barras que requiere la eliminación, no se puede escribir de forma fiable:

~Foo() 
{ 
    Bar.Dispose(); 
} 

El recolector de basura, lo hubiera recibido bar ya.

A riesgo de entrar en una pila de OO goo (es decir, comenzar una guerra), una alternativa al uso de un singleton es usar una clase estática.

+0

+1 para clase estática: los singletons no son tan necesarios como su ubicuidad sugeriría. –

+0

Conceptualmente, la única diferencia entre un singleton y una clase estática es que un singleton es un campo estático que apunta a muchos campos de instancia, donde una clase estática es un conjunto de campos estáticos. Cuando se trata de la gestión de los recursos, las diferencias entre los 2 son insignificantes. –

+0

lo que dijo scott: los singletons desechables son solo un problema fundamental – annakata

2

Si bien puede obtener revisiones de código y advertencias de FxCop, no hay nada intrínsecamente incorrecto al implementar un finalizador sin IDisposable. Sin embargo, hacerlo en un singleton no es una forma confiable de capturar procesos o eliminación de AppDomain.

A riesgo de ofrecer consejos de diseño subjetivos: si el objeto es realmente sin estado, hágalo una clase estática. Si es con estado, pregunta por qué es un Singleton: estás creando una variable global mutable. Si intenta capturar aplicaciones cerradas, trate con ellas cuando salga su bucle principal.

+0

"Si el objeto es verdaderamente sin estado, conviértalo en una clase estática. Si es estable, pregunta por qué es un Singleton" Estoy completamente de acuerdo, pero me gusta la ligereza de lo citado. ¿Tienes una referencia para eso? – annakata

+0

Lo hice sobre la marcha. Desde hace algún tiempo he estado criticando la mayoría de los usos de Singletons en el Portland Pattern Repository debido a su efecto sobre la causalidad en el código. –

0

Aplicabilidad de Singleton a cualquier situación particular, a un lado,

Creo que no hay nada malo con Eliminación de S ingleton. En combinación con la creación de instancias perezosas, solo significa que libera recursos si no los necesita temporalmente y luego vuelve a adquirirlos según sea necesario.

+0

Ese único código puede adquirir y "Descartar" un singleton no dice nada sobre si alguien más todavía podría estar usándolo. Si se llama al finalizador, nadie lo está usando; tenga en cuenta que la única manera que probablemente sucedería sería si la referencia estática/global al singleton fuera una WeakReference. De lo contrario, esa referencia en sí protegería al singleton de la finalización. – supercat

0

Si desea crear un singleton con un finalizador, probablemente debería tener la referencia estática para que sea una WeakReference. Esto requerirá un poco de trabajo adicional para garantizar la seguridad del hilo en el acceso, pero permitirá que el singleton sea recogido cuando nadie lo esté utilizando (si alguien llama posteriormente al método GetInstance(), obtendrán un nueva instancia). Si se utilizara una referencia fuerte estática, mantendría activa la instancia de singleton incluso si no hubiera otras referencias a ella.

Cuestiones relacionadas