8

Quindi sto lavorando al mio contenitore DI/IoC OpenNETCF.IoC e ho una (ragionevole) richiesta di funzionalità per aggiungere una qualche forma di gestione del ciclo di vita per gli elementi IDisposable nelle raccolte contenitore.DI: gestione della vita di oggetti idisposti

Il mio pensiero corrente è che, dal momento che non posso interrogare un oggetto per vedere se è stato eliminato, e non riesco a ottenere un evento per quando è stato eliminato, devo creare qualche forma di wrapper per gli oggetti che uno sviluppatore vuole che la struttura sia gestita.

In questo momento gli oggetti possono essere aggiunti con AddNew (per semplicità si suppone c'è solo un sovraccarico e non c'è Add):

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

Quello che sto considerando è l'aggiunta di un nuovo metodo (bene gruppo di loro, ma si ottiene l'immagine):

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

Qualora il DisposableWrappedObject assomiglia a questo:

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; 
     } 
    } 
} 

Ora, quando un oggetto viene aggiunto al contenitore tramite AddNewDIsposable, viene anche aggiunto un eventhandler in modo che quando viene eliminato (tramite il wrapper) il framework lo rimuova dalla raccolta sottostante.

In realtà ho implementato questo e sta superando i test unitari, ma sto cercando opinioni su dove questo potrebbe essere rotto, o su come potrebbe essere reso più "amichevole" allo sviluppatore che consuma.

EDIT 1

Poiché non vi era una domanda su come viene utilizzato l'evento Smaltimento, ecco qualche codice (tagliato a ciò che è 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

Possiamo ottenere una descrizione più completa della funzione specifica che si aggiunge? Quali sono i casi d'uso per cui la gente vuole questo, è il gestore di eventi per te come il (framework IoC) o l'utente finale? Ecc .. – Quibblesome

+0

Il caso d'uso è di aggiungere la gestione del ciclo di vita automatizzata. Se si aggiunge un elemento IDisposable a una raccolta e successivamente si chiama Dispose, esso non verrà mai ripulito in quanto il contenitore contiene una radice per l'oggetto. L'idea è che puoi chiamare Dispose sull'oggetto senza dover tornare alla raccolta per trovarlo, e farlo automaticamente causa la rimozione dalla raccolta del contenitore. L'evento viene utilizzato internamente dal framework (è persino contrassegnato come interno per non essere utilizzato all'esterno) e il gestore lo rimuove dalla raccolta. – ctacke

+0

Ho aggiornato la domanda per aggiungere la gestione degli eventi per maggiore chiarezza. – ctacke

risposta

3

forse mi manca qualcosa, ma perché aggiungere nuovi metodi all'API? Quando un oggetto viene aggiunto al contenitore, puoi eseguire l'as-cast per verificare se è idisposto e gestirlo in modo appropriato in caso affermativo.

Mi chiedo anche se è necessario il distruttore. Presumendo che il contenitore sia IDisposable (come Unity's), potresti semplicemente implementare lo Basic Dispose Pattern e risparmiare un sacco di overhead GC.

Alcune domande che possono essere applicabili:

+0

Ah, ma con un cast, quindi cosa? So che è IDisposable e deve essere messo in un contenitore, ma non posso restituire detto contenitore perché il metodo AddNew <> deve restituire il tipo di input. Se restituisco l'oggetto direttamente come l'API vuole, non sanno che è stato spostato, quindi chiama semplicemente Dispose sull'istanza, non sul contenitore, e di nuovo ho il problema di tenere oggetti Disposed che non ricevono mai il GC. – ctacke

+5

@ctacke: ci sono alcune regole che i consumatori devono seguire quando giocano con IoC: quando si trasferisce la responsabilità di * creare * oggetti in un contenitore DI, è necessario distribuire la gestione a vita * all *, inclusa la * distruzione * delle istanze . Pertanto, sarebbe un errore da parte del consumatore se smaltire prematuramente una dipendenza iniettata, poiché tale istanza potrebbe essere condivisa tra più consumatori. Non penso che sia necessario complicare eccessivamente la tua API per salvaguardare l'utilizzo che è semplicemente sbagliato. –