C # Singleton с конструктором, который принимает параметры - PullRequest
21 голосов
/ 14 июля 2009

Я хочу создать статический класс или одноэлементный класс, который принимает ссылку на другой объект в своем конструкторе. Статические классы отсутствуют, но я подумал, что мог бы создать синглтон, который принимает параметры в своем конструкторе. До сих пор мне не повезло выяснить или погуглить синтаксис. Это возможно? если да, то как мне это сделать?

Извините за отсутствие примера в первоначальном посте, я написал это в спешке. У меня такое ощущение, что мой ответ уже есть в ответах, но вот некоторые разъяснения того, что я хочу сделать:

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

Например, мне может понадобиться создать класс Singleton «Status», которому принадлежит объект StringBuilder и метод Draw (), который можно вызвать для записи указанного StringBuilder на экран. Метод Draw () должен знать о моем GraphcisDevice для рисования. Так что я хочу сделать это так:

public class Status
{
private static Status _instance;
private StringBuilder _messages;
private GraphicsDevice _gDevice;

private Status(string message, GraphicsDevice device)
{
    _messages.Append(message);
    _gDevice = device;
}

// The following isn't thread-safe

// This constructor part is what I'm trying to figure out
public static Status Instance // (GraphicsDevice device) 
    {
    get
        {
        if (_instance == null)
            {
            _instance = new Status("Test Message!", device); 
            }
        return _instance;
        }
    }

public void UpdateMessage
...

public void Draw()
    {
    // Draw my status to the screen, using _gDevice and _messages
    }
}  

По всему коду я извлекаю свой одиночный статус и вызываю его метод UpdateMessage ().

private Status _status = Status.Instance; // + pass reference to GraphicsDevice
_status.UpdateMessage("Foo!");

Затем в моем основном классе я также извлекаю синглтон и рисую его:

_status.Draw();

Да, это означает, что где бы я ни извлекал синглтон, я должен делать это, передавая ссылку на GraphicsDevice, на случай, если я впервые создаю экземпляр Singleton. И я мог / мог бы использовать другие средства для извлечения чего-то столь же фундаментального, как GraphicsDevice в моем классе Singleton, например, зарегистрировать службу в другом месте и получить эту службу в классе Status. Этот пример получился довольно надуманным - я пытаюсь выяснить, возможно ли, во-первых, что-то , как этот шаблон.

Ответы [ 5 ]

17 голосов
/ 14 июля 2009

Обычно это считается плохой идеей, потому что, если вы собираетесь принять либо ссылку на объект, либо аргумент типа, который вы планируете обернуть в оболочку, похожую на синглтон, вы не можете гарантировать, что у вас будет единственный экземпляр этого типа в AppDomain.

Весь смысл шаблона синглтона состоит в том, чтобы управлять одним экземпляром типа, чтобы мог существовать только один экземпляр этого типа. Если вы разрешаете передавать экземпляр или создаете универсального одноэлементного поставщика, вы не можете гарантировать, что ваш экземпляр является only instance.

Допустим, у меня был SingletonFactory<T>, который позволил бы мне создать синглтон вокруг любого типа, который я передаю на завод. Это было бы очень удобно и позволило бы мне сделать что-то вроде этого:

SingletonFactory<Foo>.Instance;

Но что мешает мне также сделать это:

Foo foo = new Foo();

Упс, похоже, что Foo больше не является синглтоном, так как я могу создать столько экземпляров, сколько захочу. Для того, чтобы шаблон синглтона работал, вы должны иметь возможность полностью контролировать тип, экземпляры которого необходимо ограничить. Вот почему вы не должны использовать что-либо подобное моему SingletonFactory<T>.

Примечание: То же самое относится и к неуниверсальному синглтону, который принимает экземпляр объекта. Я уверен, что вы можете экстраполировать из моего предыдущего примера множество похожих причин, по которым одноэлементная оболочка, которая принимает и ссылается на объект, также будет плохой идеей.

2 голосов
/ 14 июля 2009

То, что вы конкретно просите, выглядит так, я думаю:

public sealed class Singleton {
    static Singleton instance = null;
    static readonly object padlock = new Object();
    Object o;

    Singleton(Object _o) {
        o = _o;
    }

    public static Singleton Instance(Object _o) {
        lock (padlock) {
            if (instance == null) {
                instance = new Singleton(_o);
            }
            return instance;
        }
    }
}

Singleton s = Singleton.Instance(new Object());

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

2 голосов
/ 14 июля 2009

То, что вы описываете, является универсальным синглтоном. Это выглядит так:

public class SingletonProvider <T> where T:new()
{
    SingletonProvider() {}

    public static T Instance
    {
        get { return SingletonCreator.instance; }
    }

    class SingletonCreator
    {
        static SingletonCreator() { }

        internal static readonly T instance = new T();
    }
}

http://www.codeproject.com/KB/cs/genericsingleton.aspx

1 голос
/ 14 июля 2009

Вам нужен приватный конструктор, а затем метод getInstance, этот метод - тот, кто должен получить параметр, конструктор должен быть закрытым, и он также может иметь параметры, но getInstance должен передавать его, не вы. Кстати, что ты делаешь? Может помочь какой-то реальный пример.

0 голосов
/ 14 июля 2009

Одна вещь, которую вы можете сделать, это придерживаться найденного вами синглтона и просто выставить свойство (установщик, если необходимо, также добавить получатель) и использовать его как MySingleton.Instance.MyReference = new MyObject();

Если вам нужно ограничить использование вашего одноэлементного объекта, например, любые операции над одноэлементным объектом до того, как будет установлена ​​ссылка, должны быть недопустимыми, вы можете иметь закрытый логический флаг, например, hasBeenIntialized и установщик MyReference будет установить флаг внутри. Все остальные методы проверят флаг в начале выполнения и выдают исключение, если какой-либо метод вызывается, если hasBeenInitialized имеет значение false.

Если вам нужно поведение только для чтения, вы можете вызвать исключение в установщике MyReference, если кто-то хочет назначить другой объект, в то время как hasBeenInitialized имеет значение true.

...