2008-10-22 5 views
10

Ich habe ein Singleton, das die "statische readonly T Instance = new T();" Muster. Ich stieß jedoch auf einen Fall, bei dem T wegwerfbar ist und tatsächlich für Komponententests entsorgt werden muss. Wie kann ich dieses Muster ändern, um ein Einweg-Singleton zu unterstützen?Einweg-Singleton in C#

Die Schnittstelle Ich mag würde, ist so etwas wie:

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. 

Hinweis - das Muster Thread-sicher, natürlich sein.

Bearbeiten - für den Zweck der Produktion Code ist dies ein echtes Singleton. Die Sache ist, dass es einige Dateien sperrt, und so müssen wir es zur Säuberung in Komponententests entsorgen.

Ich würde auch ein Muster bevorzugen, das wiederverwendet werden kann, wenn möglich.

+0

Wenn Sie neue T tun können(), das bedeutet, dass Sie muss eine generische Einschränkung von T haben, die einen öffentlichen parameterlosen Konstruktor hat - in diesem Fall ist es kein Singleton, mit dem man beginnen kann. –

+0

Ich habe es nur als Muster geschrieben, es ist überhaupt nicht wirklich generisch. Was ich habe, ist eine Klasse, die ihren privaten Konstruktor aufruft und sie dem öffentlichen statischen Readonly-Feld zuweist. – ripper234

Antwort

12

Markieren Sie Release als internal und verwenden Sie das Attribut InternalsVisibleTo, um es nur Ihrer Einheitsprüfbaugruppe zur Verfügung zu stellen. Sie können das entweder tun, oder wenn Sie vorsichtig sind, dass jemand in Ihrer eigenen Assembly es ruft, können Sie es als private markieren und mit Reflektion darauf zugreifen.

Verwenden Sie in Ihrem Singleton einen Finalizer, der die Methode Dispose für die Singleton-Instanz aufruft.

Im Produktionscode verursacht nur die Entladung eines AppDomain die Entsorgung des Singleton. Im Testcode können Sie selbst einen Anruf an Release auslösen.

13

An diesem Punkt glaube ich nicht, dass ich es wirklich für ein Singleton mehr halten würde, um ehrlich zu sein.

Insbesondere, wenn ein Client ein Singleton verwendet, werden sie wirklich nicht erwarten, dass sie darüber verfügen müssen, und sie würden sich wundern, wenn jemand anderes es tun würde.

Was wird Ihr Produktionscode tun?

EDIT: Wenn Sie wirklich, wirklich diese Tests für die Einheit benötigen und nur für Unit-Tests (die in Bezug auf Design, fragwürdig klingen um ehrlich zu sein), dann könnte man Geige immer mit dem Feld Reflexion verwendet wird. Es wäre schöner, herauszufinden, ob es wirklich sein sollte ein Singleton oder ob es wirklich sein sollte, obwohl Einweg - die beiden sehr selten zusammen gehen.

+0

Es ist ein Objekt, das ich nur eine in meinem System haben möchte, und das einige nicht verwaltete Ressourcen (z. B. Dateien) enthält. – ripper234

+0

Ich persönlich würde es an die Dinge weitergeben, die es brauchen, um das Problem zu vermeiden. Das Singleton-Muster ist stark eingeschränkt, wenn es um solche Dinge geht - es ist ein Testkiller. Aber wenn das Design in Stein gemeißelt ist, sollten Sie die Hacky-Reflection-Lösung verwenden, um die Instanz zu ersetzen. –

0

Sie könnten einen verschachtelten faul Singleton verwenden (siehe here) mit einigen einfachen Änderungen:

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

Denken Sie daran, ObjectDisposedException in allen öffentlichen Methoden/Eigenschaften des Objekts zu werfen, wenn sie entsorgt wurde.

Sie sollten auch eine Finalizer-Methode für das Objekt bereitstellen, falls Dispose nicht aufgerufen wird. Sehen Sie, wie Sie IDisposable here korrekt implementieren können.

0

Wenn die Klasse IDisposable implementiert (wie Sie bedeuten es tut), dann rufen Sie einfach 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

Für Unit-Tests Sie eine „manuelle“ Instanz verwenden könnte (aber man würde einen Weg brauchen, um instanziieren Sie das Objekt).

In Ihrem Fall sollten Sie besser das Factory-Muster verwenden (Abstract/Methode - je nachdem, was am besten für Ihren Fall ist), kombiniert mit einem Singleton.

Wenn Sie testen möchten, ob das Singleton die verwendeten Objekte richtig sortiert hat (im Komponententest), verwenden Sie die Factory-Methode, andernfalls verwenden Sie das Singleton-Muster.

Übrigens, wenn Sie nicht Zugriff auf den Singleton-Quellcode haben oder Sie es nicht ändern dürfen, würden Sie besser wickeln Sie es auf ein anderes Singleton, und stellen Sie die gesamte Logik aus dem neuen (ähnlich ein Proxy). Es klingt nach Overkill, aber es könnte eine praktikable Lösung sein.

Um den Zugriff darauf zu steuern, stellen Sie eine Factory bereit, und lassen Sie die Clients das neue Objekt nur abrufen, wenn das Objekt nicht entfernt wurde.

4

Singletons sollten nicht wegwerfbar sein. Zeitraum. Wenn jemand Dispose vorzeitig anruft, wird Ihre Anwendung geschraubt, bis sie neu gestartet wird.

+8

Es gibt KEINE absoluten Werte in der Softwareentwicklung, außer es gibt keine absoluten Werte in der Softwareentwicklung. Wenn du damit klarkommst, wirst du ein besserer Ingenieur sein ... – iGanja

0

Eine weitere Möglichkeit, eine Einweg Singleton machen die Sandburg [Singleton] atribute für Ihre Klasse zu bedienen ist, dann nimmt Castle Rahmen Pflege alle Einweg Singleton-Objekte der Entsorgung