8

Estoy trabajando en mi contenedor DI/IoC OpenNETCF.IoC y tengo una solicitud (razonable) para agregar algún tipo de gestión del ciclo de vida para los artículos IDisposable en las colecciones de contenedores.DI: manejo de la vida de objetos identificables

Mi idea actual es que, como no puedo consultar un objeto para ver si ha sido eliminado, y no puedo obtener un evento para cuando haya sido eliminado, tengo que crear alguna forma de contenedor para los objetos que un desarrollador quiere que se administre el marco.

public TTypeToBuild AddNew<TTypeToBuild>() { ... } 

Lo que estoy considerando es la adición de un nuevo método (también:

En este momento los objetos pueden ser añadido con AñadirNuevo (por razones de simplicidad asumiremos que sólo hay una sobrecarga y no hay Agregar)

grupo de ellos, pero se obtiene la imagen):

public DisposableWrappedObject<IDisposable> AddNewDisposable<TTypeToBuild>() 
    where TTypeToBuild : class, IDisposable 
{ 
    ... 
} 

Cuando el DisposableWrappedObject se ve así:

public class DisposableWrappedObject<T> 
    where T : class, IDisposable 
{ 
    public bool Disposed { get; private set; } 
    public T Instance { get; private set; } 

    internal event EventHandler<GenericEventArgs<IDisposable>> Disposing; 

    internal DisposableWrappedObject(T disposableObject) 
    { 
     if (disposableObject == null) throw new ArgumentNullException(); 

     Instance = disposableObject; 
    } 

    ~DisposableWrappedObject() 
    { 
     Dispose(false); 
    } 

    public void Dispose() 
    { 
     Dispose(true); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     lock(this) 
     { 
      if(Disposed) return; 

      EventHandler<GenericEventArgs<IDisposable>> handler = Disposing; 
      if(handler != null) 
      { 
       Disposing(this, new GenericEventArgs<IDisposable>(Instance)); 
      } 

      Instance.Dispose(); 

      Disposed = true; 
     } 
    } 
} 

Ahora, cuando un elemento se agrega al contenedor mediante AddNewDIsposable, también se agrega un controlador de eventos para que cuando se disipe (a través del contenedor) el marco lo elimine de la colección subyacente.

De hecho, tengo esto implementado y está pasando las pruebas unitarias, pero estoy buscando opiniones sobre dónde podría romperse, o cómo podría hacerse más "amigable" para el desarrollador consumidor.

EDIT 1

Puesto que no era una cuestión de cómo se utiliza el evento Eliminación, aquí hay un código (que se corta a lo que es importante):

private object AddNew(Type typeToBuild, string id, bool wrapDisposables) 
{ 
    .... 

    object instance = ObjectFactory.CreateObject(typeToBuild, m_root); 

    if ((wrapDisposables) && (instance is IDisposable)) 
    { 
     DisposableWrappedObject<IDisposable> dispInstance = new 
       DisposableWrappedObject<IDisposable>(instance as IDisposable); 
     dispInstance.Disposing += new 
       EventHandler<GenericEventArgs<IDisposable>>(DisposableItemHandler); 
     Add(dispInstance as TItem, id, expectNullId); 
     instance = dispInstance; 
    } 

    .... 

    return instance; 
} 

private void DisposableItemHandler(object sender, GenericEventArgs<IDisposable> e) 
{ 
    var key = m_items.FirstOrDefault(i => i.Value == sender).Key; 
    if(key == null) return; 
    m_items.Remove(key); 
} 
+0

¿Podemos obtener una descripción más completa de la función específica que se agrega? ¿Cuáles son los casos de uso para los que la gente quiere esto, es el controlador de eventos para usted como el (marco IoC) o el usuario final? Etc .. – Quibblesome

+0

El caso de uso es para agregar la administración automática del ciclo de vida. Si agrega un artículo IDisposable a una colección y luego llama a Dispose, en realidad nunca se limpiará porque el contenedor tiene una raíz para el objeto. La idea es que puede llamar a Dispose sobre el objeto sin tener que volver a la colección para encontrarlo, y que eso provoque automáticamente la eliminación de la colección del contenedor. El evento se usa puramente internamente por el marco (incluso se marca como interno para no ser utilizado fuera) y el manejador lo elimina de la colección. – ctacke

+0

He actualizado la pregunta para agregar el manejo del evento para mayor claridad. – ctacke

Respuesta

3

Tal vez me falta algo, pero ¿por qué agregar nuevos métodos a la API? Cuando se agrega un objeto al contenedor, se puede agregar para verificar si es IDisposable y manejarlo de manera adecuada si es así.

También me pregunto si necesita el destructor. Suponiendo que el contenedor es IDisposable (como el de Unity), puede implementar el Basic Dispose Pattern y ahorrar una gran cantidad de gastos generales del GC.

Algunas preguntas que pueden ser aplicables:

+0

Ah, pero con un as-cast, ¿entonces qué? Sé que es IDisposable y debe colocarse en un contenedor, pero no puedo devolver dicho contenedor porque el método AddNew <> debe devolver el tipo de entrada. Si devuelvo el objeto directamente como lo desea la API, no saben que estaba empaquetado, y luego simplemente llaman a Dispose en la instancia, no en el contenedor, y nuevamente tengo el problema de mantener objetos Dispuestos que nunca reciben GC. – ctacke

+5

@ctacke: Hay algunas reglas que los consumidores deben seguir cuando juegan con IoC: cuando se le entrega la responsabilidad de * crear * objetos a un Contenedor DI, debe entregar * toda * administración de por vida, incluyendo * destrucción * de instancias . Por lo tanto, sería un error del lado del consumidor si eliminan prematuramente una dependencia inyectada, ya que esa instancia puede compartirse entre múltiples consumidores. No creo que tenga que complicar demasiado su API para protegerse del uso que es simplemente incorrecto. –

Cuestiones relacionadas