Одноразовый синглтон в C # - PullRequest
11 голосов
/ 22 октября 2008

У меня есть синглтон, который использует «статический только для чтения 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.

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

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

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

Ответы [ 8 ]

13 голосов
/ 22 октября 2008

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

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

Что будет делать ваш рабочий код?

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

12 голосов
/ 22 октября 2008

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

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

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

4 голосов
/ 22 октября 2008

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

1 голос
/ 22 октября 2008
 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 голосов
/ 15 апреля 2009

Еще один вариант изготовления одноразового синглтона - использовать атрибут SandCastle [Singleton] для вашего класса, а затем Castle Framework позаботится об удалении всех одноразовых объектов Singleton

.
0 голосов
/ 22 октября 2008

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

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

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

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

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

0 голосов
/ 22 октября 2008

Если класс реализует IDisposable (как вы подразумеваете), тогда просто вызовите x.Dispose ()

0 голосов
/ 22 октября 2008

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

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 здесь .

...