Одноразовый синглтон в С#
У меня есть singleton, который использует "static readonly T Instance = new T();" шаблон. Однако я столкнулся с ситуацией, когда T является одноразовым, и на самом деле его нужно утилизировать для модульных тестов. Как изменить этот шаблон для поддержки одноразового одноэлемента?
Мне нужен интерфейс:
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.
Примечание - шаблон должен быть потокобезопасным, конечно.
Изменить - для производственного кода это настоящий синглтон. Дело в том, что он блокирует некоторые файлы, и поэтому для очистки в модульных тестах мы должны утилизировать его.
Я также предпочел бы шаблон, который можно использовать повторно, если это возможно.
Ответы
Ответ 1
Отметьте Release
как internal
и используйте атрибут InternalsVisibleTo
, чтобы выставить его только на сборку тестирования устройства. Вы можете это сделать, или если вы опасаетесь, что кто-то из вашей собственной сборки вызовет его, вы можете пометить его как private
и получить доступ к нему с помощью отражения.
Используйте финализатор в одном синглете, который вызывает метод Dispose
для экземпляра singleton.
В производственном коде только выгрузка AppDomain
приведет к удалению синглтона. В тестовом коде вы можете инициировать вызов Release
самостоятельно.
Ответ 2
В этот момент я не думаю, что, если честно, я действительно считаю его синглом.
В частности, если клиент использует синглтон, они действительно не собираются ожидать, что им придется избавиться от него, и они были бы удивлены, если бы кто-то еще это сделал.
Каким будет ваш производственный код?
EDIT: Если вам действительно действительно нужно это для модульных тестов и только для модульных тестов (что кажется сомнительным с точки зрения дизайна, если быть откровенным), тогда вы всегда можете играть с полем, используя отражение. Было бы лучше разобраться, действительно ли это синглтон или действительно ли он должен быть одноразовым, хотя два очень редко идут вместе.
Ответ 3
Синглтоны не должны быть одноразовыми. Период. Если кто-то вызывает Dispose преждевременно, ваше приложение вкручивается до перезапуска.
Ответ 4
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;}
}
Ответ 5
Вы можете использовать вложенный ленивый синглтон (см. здесь) с некоторыми простыми модификациями:
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 во все общедоступные методы/свойства объекта, если он был удален.
Вы также должны предоставить метод finalizer для объекта, если Dispose не вызван. Посмотрите, как правильно реализовать IDisposable здесь.
Ответ 6
Если класс реализует IDisposable (как вы это предполагаете), просто вызовите x.Dispose()
Ответ 7
Для модульных тестов вы можете использовать "ручной" экземпляр (но вам нужен способ создания экземпляра объекта).
В вашем случае, вероятно, вам лучше использовать шаблон factory (абстрактный/метод - в зависимости от того, что лучше для вашего случая), в сочетании с синглоном.
Если вы хотите проверить, правильно ли размещен одиночный элемент для используемых объектов (в unit test), используйте метод factory, иначе используйте шаблон singleton.
Кстати, если у вас нет доступа к однопользовательскому исходному коду, или вам не разрешено его модифицировать, вы можете перенести его на другой синглтон и предоставить всю логику из новой (более похожую на прокси). Это звучит как излишний, но это может быть жизнеспособным решением.
Кроме того, чтобы иметь возможность контролировать доступ к нему, укажите factory, и пусть клиенты получат новый объект только в том случае, если объект не был удален.
Ответ 8
Еще одна возможность сделать одноразовый Singleton - использовать атрибут SandCastle [Singleton] для вашего класса, тогда Castle framework заботится об утилизации всех одноразовых объектов Singleton