8

So arbeite ich an meinem DI/IoC-Container OpenNETCF.IoC und ich habe eine (sinnvolle) Feature-Anfrage, um eine Form von Lifecycle-Management für IDisposable Elemente in den Containersammlungen hinzuzufügen.DI: Behandeln der Lebensdauer von IDisposable Objects

Mein aktuelles Denken ist, dass, da ich ein Objekt nicht abfragen kann, um zu sehen, ob es entsorgt wurde, und ich kein Ereignis abrufen kann, wenn es entsorgt wurde, dass ich eine Art Wrapper für Objekte erstellen muss dass ein Entwickler das Framework verwalten möchte.

Rechts-Objekte können jetzt mit AddNew hinzugefügt werden (der Einfachheit halber nehmen wir an, es gibt nur eine Überlastung und es gibt keine Add):

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

Was ich erwägt eine neue Methode Hinzufügen (gut Gruppe von ihnen, aber Sie bekommen das Bild):

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

Wo die DisposableWrappedObject wie folgt aussieht:

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

Wenn nun ein Element über AddNewDIsposable zum Container hinzugefügt wird, wird auch ein Eventhandler hinzugefügt, so dass das Framework es aus der zugrunde liegenden Sammlung entfernt, sobald es Disposed (über den Wrapper) erhält.

Ich habe dies tatsächlich implementiert und es besteht die Unit-Tests, aber ich bin auf der Suche nach Meinungen darüber, wo dies möglicherweise gebrochen wird, oder wie es für den konsumierenden Entwickler "freundlicher" gemacht werden könnte.

EDIT 1

Da es eine Frage, wie die Entsorgung Ereignis verwendet wird, hier einige Code (getrimmt, was wichtig ist):

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

Können wir Beschreibung der spezifischen Funktion erhalten eine vollere, die hinzugefügt wird? Was sind die Anwendungsfälle, für die die Leute das wollen, ist der Event-Handler für Sie als (IoC-Framework) oder als Endbenutzer? Usw. – Quibblesome

+0

Der Anwendungsfall ist das Hinzufügen eines automatisierten Lebenszyklusmanagements. Wenn Sie ein IDisposable-Objekt zu einer Sammlung hinzufügen und später Dispose aufrufen, wird es niemals wirklich bereinigt, da der Container eine Wurzel für das Objekt enthält. Die Idee ist, dass Sie Dispose für das Objekt aufrufen können, ohne zur Sammlung zurückkehren zu müssen, um es zu finden, und dies automatisch zur Entfernung aus der Containersammlung führt. Das Ereignis wird rein intern vom Framework verwendet (es ist sogar als intern markiert, damit es nicht verwendet wird) und der Handler entfernt es aus der Sammlung. – ctacke

+0

Ich habe die Frage aktualisiert, um die Ereignisbehandlung für Klarheit hinzuzufügen. – ctacke

Antwort

3

Vielleicht bin ich etwas fehlt, aber Warum fügen Sie der API neue Methoden hinzu? Wenn ein Objekt zum Container hinzugefügt wird, können Sie es als Vorlage verwenden, um zu prüfen, ob es IDisposable ist, und es gegebenenfalls zu behandeln.

Ich frage mich auch, ob Sie den Destruktor brauchen. Angenommen, der Container ist IDisposable (wie bei Unity), können Sie einfach Basic Dispose Pattern implementieren und viel GC-Overhead sparen.

einige Fragen, die anwendbar sein können:

+0

Ah, aber mit einem As-Cast, was dann? Ich weiß, dass es IDisposable ist und in einen Container eingegeben werden muss, aber ich kann diesen Container nicht zurückgeben, da die AddNew <> -Methode den Eingabetyp zurückgeben muss. Wenn ich das Objekt direkt zurücksende, wie es die API will, wissen sie nicht, dass es umgebrochen wurde, und dann einfach Dispose für die Instanz aufrufen, nicht den Container, und wieder habe ich das Problem, disponierte Objekte zu halten, die niemals GC's erhalten. – ctacke

+5

@ctacke: Es gibt einige Regeln, denen die Konsumenten folgen müssen, wenn sie mit IoC spielen: Wenn Sie die Verantwortung für * creating * Objekte an einen DI Container delegieren, müssen Sie * alle * Lifetime Managment, einschließlich * Destruction * der Instanzen, übergeben . Daher wäre es ein Fehler auf der Seite des Verbrauchers, wenn sie eine injizierte Abhängigkeit vorzeitig beseitigen, da diese Instanz unter mehreren Verbrauchern geteilt werden kann. Ich glaube nicht, dass Sie Ihre API übermäßig komplizieren müssen, um eine Nutzung zu verhindern, die einfach falsch ist. –