8

Je travaille donc sur mon conteneur DI/IoC OpenNETCF.IoC et j'ai une demande de fonctionnalité (raisonnable) pour ajouter une forme de gestion du cycle de vie pour les éléments IDisposable dans les collections de conteneurs. Ma pensée actuelle est que, puisque je ne peux pas interroger un objet pour voir s'il a été éliminé, et je ne peux pas obtenir un événement pour quand il a été éliminé, que je dois créer une forme d'emballage pour les objets qu'un développeur veut que le framework gère.DI: Gestion de la vie des objets IDisposable

objets En ce moment, on peut ajouter avec AddNew (par souci de simplicité, nous supposons qu'il n'y a qu'une surcharge et il n'y a pas Ajouter):

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

Ce que je considère est l'ajout d'une nouvelle méthode (bien groupe d'entre eux, mais vous obtenez l'image):

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

Lorsque le DisposableWrappedObject ressemble à ceci:

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

Maintenant, lorsqu'un élément est ajouté au conteneur via AddNewDIsposable, un gestionnaire d'événements est également ajouté de sorte que lorsqu'il est mis au rebut (via l'encapsuleur), le framework le supprime de la collection sous-jacente. En fait, j'ai implémenté cela et il passe les tests unitaires, mais je cherche des avis sur où cela pourrait être cassé, ou comment il pourrait être rendu plus "amical" pour le développeur consommateur.

EDIT 1

Comme il y avait une question sur la façon dont l'événement cédante est utilisé, voici un code (coupé à ce qui est important):

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

Pouvons-nous obtenir une description plus complète de la fonction spécifique qui est ajouté? Quels sont les cas d'utilisation pour lesquels les gens veulent cela, est le gestionnaire d'événements pour vous en tant que (IoC framework) ou l'utilisateur final? Etc .. – Quibblesome

+0

Le cas d'utilisation consiste à ajouter une gestion automatisée du cycle de vie. Si vous ajoutez un élément IDisposable à une collection et appelez ensuite Dispose, il ne sera jamais nettoyé car le conteneur contient une racine de l'objet. L'idée est que vous pouvez appeler Dispose sur l'objet sans avoir à revenir à la collection pour le trouver, et que cela entraîne automatiquement la suppression de la collection de conteneurs. L'événement est utilisé uniquement en interne par le framework (il est même marqué comme interne pour ne pas être utilisé à l'extérieur) et le gestionnaire le supprime de la collection. – ctacke

+0

J'ai mis à jour la question pour ajouter la gestion des événements pour plus de clarté. – ctacke

Répondre

3

Peut-être que je manque quelque chose, mais pourquoi ajouter de nouvelles méthodes à l'API? Lorsqu'un objet est ajouté au conteneur, vous pouvez l'analyser pour vérifier s'il est IDisposable et le gérer de manière appropriée si c'est le cas. Je me demande aussi si vous avez besoin du destructeur. En supposant que le conteneur est IDisposable (comme celui de Unity), vous pouvez simplement implémenter le Basic Dispose Pattern et économiser beaucoup de temps système.

Certaines questions qui peuvent être applicables:

+0

Ah, mais avec un moulage, alors quoi? Je sais que c'est IDisposable et doit être mis dans un conteneur, mais je ne peux pas retourner ce conteneur parce que la méthode AddNew <> doit retourner le type d'entrée. Si je retourne l'objet directement comme le veut l'API, ils ne savent pas qu'il a été enveloppé, puis appellent simplement Dispose sur l'instance, et non sur le conteneur, et j'ai de nouveau le problème de contenir des objets Disposed qui ne sont jamais GCés. – ctacke

+5

@ctacke: Il y a certaines règles que les consommateurs doivent suivre quand ils jouent avec IoC: Quand vous confiez la responsabilité de * créer * des objets à un conteneur DI, vous devez transférer * toute * gestion de la vie, y compris * destruction * des instances . Ainsi, ce serait une erreur du côté des consommateurs s'ils éliminent prématurément une dépendance injectée, puisque cet exemple peut être partagé entre plusieurs consommateurs. Je ne pense pas que vous deviez trop compliquer votre API pour vous protéger contre une utilisation qui est tout simplement erronée. –