2008-10-22 6 views
10

J'ai un singleton qui utilise le "statique T instance en instance = new T();" modèle. Cependant, je suis tombé sur un cas où T est jetable, et doit effectivement être disposé pour des tests unitaires. Comment puis-je modifier ce modèle pour soutenir un singleton jetable?Singleton jetable en C#

L'interface que je voudrais est quelque chose comme:

var x = Foo.Instance; 
var y = Foo.Instance; // x == y 
... 
x.Release(); // this causes the next Foo.Instance to return a fresh object 
      // also, it assumes no further operations on x/y will be performed. 

Note - le modèle doit être thread-safe, bien sûr.

Édition - pour les besoins du code de production, il s'agit d'un vrai singleton. Le truc c'est que ça bloque certains fichiers, et donc pour le nettoyage dans les tests unitaires nous devons le jeter.

Je préférerais également un modèle qui peut être réutilisé, si possible.

+0

Si vous pouvez faire de nouveaux T() cela signifie que vous doit avoir une contrainte générique de T ayant un constructeur public sans paramètre - auquel cas ce n'est pas un singleton pour commencer. –

+0

Je viens de l'écrire comme un motif, ce n'est pas vraiment générique en ce moment. Ce que j'ai, c'est une classe qui appelle son constructeur privé et l'assigne au champ public static readonly. – ripper234

Répondre

12

Marquez Release comme internal et utilisez l'attribut InternalsVisibleTo pour l'exposer uniquement à l'ensemble de test de votre unité. Vous pouvez le faire, ou si vous vous méfiez de quelqu'un dans votre propre assemblée, vous pouvez le marquer comme private et y accéder en utilisant la réflexion.

Utilisez un finaliseur dans votre singleton qui appelle la méthode Dispose sur l'instance singleton.

En code de production, seul le déchargement d'un AppDomain entraîne l'élimination du singleton. Dans le code de test, vous pouvez lancer un appel au Release vous-même.

13

À ce stade, je ne pense pas que je considérerais vraiment cela comme un singleton, pour être honnête.

En particulier, si un client utilise un singleton, il ne s'attend pas à ce qu'il doive en disposer, et il serait surpris que quelqu'un d'autre le fasse.

Que fera votre code de production? EDIT: Si vous en avez vraiment, vraiment besoin pour les tests unitaires et seulement pour les tests unitaires (ce qui semble discutable en termes de design, pour être franc), vous pouvez toujours jouer avec le champ en utilisant la réflexion. Il serait plus agréable de savoir si cela devrait vraiment être un singleton ou si elle devrait vraiment être jetable - les deux vont très rarement ensemble.

+0

C'est un objet que je voudrais avoir un seul dans mon système, et qui contient des ressources non gérées (par exemple des fichiers). – ripper234

+0

Personnellement, je le passerais aux choses qui en ont besoin, en évitant le problème pour commencer. Le modèle singleton est sévèrement limité quand il s'agit de ce genre de chose - c'est un test tueur. Mais étant donné que le design est coulé dans la pierre, optez pour la solution de réflexion hacky pour remplacer l'instance. –

0

Vous pouvez utiliser un singleton paresseux imbriquée (voir here) avec quelques modifications simples:

public sealed class Singleton : IDisposable 
{ 
    Singleton() 
    { 
    } 

    public static Singleton Instance 
    { 
     get 
     { 
      if (!Nested.released) 
       return Nested.instance; 
      else 
       throw new ObjectDisposedException(); 
     } 
    } 

    public void Dispose() 
    { 
     disposed = true; 
     // Do release stuff here 
    } 

    private bool disposed = false; 

    class Nested 
    { 
     // Explicit static constructor to tell C# compiler 
     // not to mark type as beforefieldinit 
     static Nested() 
     { 
     } 

     internal static readonly Singleton instance = new Singleton(); 
    } 
} 

Rappelez-vous de jeter ObjectDisposedException dans toutes les méthodes publiques/propriétés de l'objet si elle a été disposé.

Vous devez également fournir une méthode de finalisation pour l'objet, dans le cas où Dispose n'est pas appelé. Voir comment implémenter correctement IDisposable here.

0

Si la classe implémente IDisposable (comme vous le laissez entendre qu'il fait), alors il suffit d'appeler x.Dispose()

1
public class Foo : IDisposable 
    { [ThreadStatic] static Foo _instance = null; 

    private Foo() {IsReleased = false;} 

    public static Foo Instance 
    { get 
     { if (_instance == null) _instance = new Foo(); 
      return _instance; 
     } 
    } 

    public void Release() 
    { IsReleased = true; 
     Foo._instance = null; 
    } 

    void IDisposable.Dispose() { Release(); } 

    public bool IsReleased { get; private set;} 

    } 
0

Pour les tests unitaires, vous pouvez utiliser une instance « manuel » (mais vous auriez besoin d'un moyen de instancier l'objet).

Dans votre cas, vous devriez probablement utiliser le motif d'usine (résumé/méthode - selon ce qui convient le mieux à votre cas), combiné avec un singleton.

Si vous voulez tester si le singleton s'est correctement débarrassé des objets utilisés (en test unitaire), utilisez la méthode Factory, sinon utilisez le pattern singleton. Par ailleurs, si vous n'avez pas accès au code source de Singleton ou si vous n'êtes pas autorisé à le modifier, vous devriez l'inclure dans un autre singleton et lui fournir toute la logique du nouveau (plus comme un proxy). Cela semble exagéré, mais cela pourrait être une solution viable.

De même, afin de pouvoir en contrôler l'accès, fournissez une fabrique et laissez les clients récupérer le nouvel objet uniquement si l'objet n'a pas été éliminé.

4

Les singletons ne doivent pas être jetables. Période. Si quelqu'un appelle Dispose prématurément, votre application est vissée jusqu'à ce qu'elle redémarre.

+8

Il n'y a AUCUN absolu en génie logiciel, sauf qu'il n'y a pas d'absolus dans l'ingénierie logicielle. Quand vous aurez compris cela, vous serez un meilleur ingénieur ... – iGanja

0

Une autre option pour faire un Singleton à usage unique est d'utiliser SandCastle [Singleton] atribute pour votre classe, puis cadre Castle prend soin de disposer tous les Singleton jetables objets