Вопрос

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top