Одноразовый синглтон в С#

У меня есть 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