Одноразовый синглтон в C #
Вопрос
У меня есть синглтон, в котором используется " статическое чтение только 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.
Примечание. Конечно, шаблон должен быть поточно-ориентированным.
Изменить - для производственного кода это настоящий синглтон. Дело в том, что он блокирует некоторые файлы, и поэтому для очистки в модульных тестах мы должны его утилизировать.
Я бы также предпочел шаблон, который можно использовать повторно, если это возможно.
Решение
Пометьте Release
как internal
и используйте атрибут InternalsVisibleTo
, чтобы предоставить его только вашей сборке модульного тестирования. Вы можете сделать это, или если вы опасаетесь, что кто-то в вашей сборке вызовет его, вы можете пометить его как private
и получить к нему доступ с помощью отражения.
Используйте финализатор в вашем синглтоне, который вызывает метод Dispose
для экземпляра синглтона.
В рабочем коде только выгрузка AppDomain
приведет к удалению синглтона. В тестовом коде вы можете инициировать вызов Release
самостоятельно.
Другие советы
В тот момент я не думаю, что на самом деле считаю, что это больше одиночка, если честно.
В частности, если клиент использует синглтон, он на самом деле не будет ожидать, что ему придется им распоряжаться, и он будет удивлен, если кто-то другой это сделает.
Что будет делать ваш рабочий код?
РЕДАКТИРОВАТЬ: если вам действительно нужно это для модульных тестов и только для модульных тестов (что звучит сомнительно с точки зрения дизайна, если честно), то вы всегда можете поиграть с полем, используя отражение , Было бы лучше выяснить, должен ли он действительно быть синглтоном, или же он должен действительно быть одноразовым - эти два очень редко идут вместе.
Синглтоны не должны быть одноразовыми. Период. Если кто-то вызывает Dispose преждевременно, ваше приложение закручено, пока оно не перезапустится Р>
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;}
}
Если класс реализует IDisposable (как вы подразумеваете), тогда просто вызовите x.Dispose ()
Вы можете использовать вложенный ленивый синглтон (см. здесь ) с некоторыми простые модификации:
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 здесь . р>
Для модульных тестов вы можете использовать " руководство " экземпляр (но вам нужен способ создания экземпляра объекта).
В вашем случае, вероятно, вам лучше использовать фабричный шаблон (аннотация / метод - в зависимости от того, что лучше для вашего случая) в сочетании с синглтоном.
Если вы хотите проверить, правильно ли синглтон утилизировал использованные объекты (в модульном тесте), используйте метод Factory, в противном случае используйте шаблон синглтона.
Кстати, если у вас нет доступа к исходному коду синглтона или вам не разрешено изменять его, вам лучше связать его с другим синглтоном и предоставить всю логику из нового (более похожего на прокси). Это звучит как излишество, но это может быть жизнеспособным решением.
Кроме того, чтобы иметь возможность контролировать доступ к нему, предоставьте фабрику и разрешите клиентам получать новый объект, только если объект не был утилизирован.
Еще один вариант создания одноразового Singleton - использовать атрибут [Singleton] SandCastle для вашего класса, а затем Castle Framework избавится от всех одноразовых объектов Singleton