2008-10-22 2 views
10

У меня есть синглтон, который использует «статический readonly T Instance = new T();» шаблон. Однако я столкнулся с ситуацией, когда T является одноразовым, и на самом деле его нужно утилизировать для модульных тестов. Как изменить этот шаблон для поддержки одноразового одноразового использования?Одноразовый синглтон в C#

Интерфейс Я хотел бы что-то вроде:

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. 

Примечание - шаблон должен быть поточно-, конечно.

Редактировать - для целей производственного кода это настоящий синглтон. Дело в том, что он блокирует некоторые файлы, и поэтому для очистки в модульных тестах мы должны утилизировать его.

Я также предпочел бы образец, который можно использовать повторно, если это возможно.

+0

Если вы можете сделать новый T(), что означает, что вы должен иметь общее ограничение T, имеющее открытый конструктор без параметров, и в этом случае это не одноэлементное начало. – 2008-10-22 13:30:11

+0

Я просто написал это как образец, на самом деле он не является общим. У меня есть класс, который вызывает его частный конструктор и назначает его публичному статическому полю readonly. – ripper234 2008-10-22 13:32:15

ответ

12

Отметьте Release как internal и используйте атрибут InternalsVisibleTo, чтобы разоблачить его только на единицу тестирования. Вы можете это сделать, или если вы опасаетесь, что кто-то в вашей собственной сборке вызовет его, вы можете пометить его как private и получить к нему доступ с помощью отражения.

Используйте финализатор в своем одиночном тоне, который вызывает метод Dispose для экземпляра singleton.

В производственном кодексе только выгрузка AppDomain приведет к удалению синглтона. В тестовом коде вы можете инициировать вызов до Release самостоятельно.

13

В этот момент я не думаю, что, если честно, я действительно считаю его синглом.

В частности, если клиент использует синглтон, они действительно не собираются ожидать, что им придется избавиться от него, и они были бы удивлены, если бы кто-то еще это сделал.

Что будет делать ваш производственный код?

РЕДАКТИРОВАТЬ: Если вам действительно нужно это для модульных испытаний и только для юнит-тестов (что будет звучать сомнительно с точки зрения дизайна, если быть откровенным), тогда вы всегда можете играть в поле с использованием отражения. Было бы лучше разобраться, должно ли оно действительно быть синглом или должно ли оно действительно быть одноразовым, хотя - два очень редко идут вместе.

+0

Это объект, который я хотел бы иметь только один в моей системе и который содержит некоторые неуправляемые ресурсы (например, файлы). – ripper234 2008-10-22 13:50:50

0

Вы можете использовать вложенный ленивый синглтон (см here) с некоторыми простыми модификациями:

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

Не забудьте бросить ObjectDisposedException во всех общественных методах/свойствах объекта, если он был удален.

Вы также должны предоставить метод финализатора для объекта, если Dispose не вызван. Посмотрите, как правильно реализовать IDisposable here.

0

Если класс реализует IDisposable (как это вы подразумеваете это делает), то просто позвоните 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

Для модульных тестов вы можете использовать «ручной» экземпляр (но вам нужно будет путь к экземпляр объекта).

В вашем случае, возможно, вам лучше использовать шаблон фабрики (аннотация/метод - в зависимости от того, что лучше для вашего случая), в сочетании с одноэлементным.

Если вы хотите проверить, правильно ли выбрана синглтон из использованных объектов (в модульном тесте), используйте метод Factory, иначе используйте шаблон singleton.

Кстати, если у вас нет доступа к однопользовательскому исходному коду или вам не разрешено его модифицировать, вы можете перенести его на другой синглтон и предоставить всю логику из новой (более похожее прокси). Это звучит как перебор, но это может быть жизнеспособным решением.

Кроме того, чтобы иметь возможность контролировать доступ к нему, предоставить фабрику и позволить клиентам получить новый объект только в том случае, если объект не был удален.

4

Синглтоны не должны быть одноразовые. Период. Если кто-то вызывает Dispose преждевременно, ваше приложение вкручивается до перезапуска.

0

Другого вариантом, чтобы сделать одноразовый Singleton является использование SandCastle в [Singleton] atribute для вашего класса, то замок рамка ухаживает увод всех одноразовых Singleton объектов

Смежные вопросы