简体   繁体   中英

Possible to use a singleton with a non-default constructor in C#?

This is a slight variance of this question: Possible to use a singleton with a non-default constructor in C#?

I have a class that takes parameters for it's constructor. I would like to make this singleton such that the parameters are taken upon initialising the singleton and thus would not need to be passed in each time the instance is retrieved.

My solution (which is not elegant) for this is to have a CreateInstance() static method that takes the parameters and constructs the singleton instance. I would then have another static method GetInstance() which would be parameterless to obtain the singleton instance. In code, I would then need to ensure the logic calls CreateInstance before any calls to GetInstance. However, I cannot enforce this at compile time. I can, however, check at runtime by throwing an exception in GetInstance if it is called before CreateInstance .

Is there anyway I can achieve this behaviour with compile time enforcement? Or at the very least, is there a better way of doing the same thing?

There is no way to do it at compile time, because that would be like asking the compiler "can you prove that code X is never executed before code Y is executed, in the presence of multiple threads?". It cannot be done.

As for the runtime behavior of your design, I think this is as good as it can ever be.

You can make it slightly better by exposing a Func<SingletonType> property in your singleton class. When someone asks for the singleton instance and the instance has not already been created, your class would call this "factory method" to construct the singleton. If the factory method is null , then you either throw an exception or (if applicable) construct using some default parameters.

What this does is essentially defer the construction of the singleton until it's actually needed for the first time, so it's some improvement. But the underlying principle is the same.

Update:

As LukeH points out, this is pretty much what Lazy<T> does (.NET 4 only). If possible, use that one instead of writing your own.

In a classic singleton, the real magic happens in static readonly which creates the instance as soon as it is used:

public class MySingleton
{
    private static readonly _instance = new MySingleton();

    private MySingleton() {}

    public static MySingleton Instance
    {
        get
        {
            return _instance;
        }
    }

}

If you have parameters to pass to constructor, you have to implement locking yourself (note the double if sandwitching the lock ):

public class MySingletonWithConstructor
{
    private static _instance;
    private static object _lock = new Object();

    private MySingletonWithConstructor(string myArg) 
    {
        // ... do whatever necessary
    }

    public static MySingletonWithConstructor Instance
    {
        get
        {
            if(_instance==null)
            {
                lock(_lock)
                {
                    if(_instance==null) // double if to prevent race condition
                    {
                        _instance = new MySingletonWithConstructor("Something");
                    }
                }
            }
            return _instance;
        }
    }

}

如果单例对象不存在,您可以让GetInstance()调用CreateInstance()方法。

I would do it similar to this. You may have to add locks or other things to ensure:

 public class ClassA {     
    private static ClassA instance;

    private int param;

    private ClassA(int param) {
       this.param = param;
    }

    public static ClassA getInstance() {
       if (instance == null) {
          throw new CustomException("Not yet initialised");
       } else {
          return instance;
       }
    }

    public static void createInstance(int param) {
       if (instance == null) {
          instance = new ClassA(param);
       }
    }
}

在你的GetInstance()方法中,如果你的值为null,为什么不调用CreateInstance,那么你就是懒惰的初始化...

使用CreateInstance()作为Lazy<T>的加载器并让GetInstance返回Lazy.Value(您可能希望创建一个静态只读字段,设置为= thelazy.Value以确保单个条目进入CreateInstance())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM