简体   繁体   中英

Question about non-static members of a singleton (C#)

I have a question about singletons that I think I know the answer to...but every time the scenario pops-up I kinda second guess myself a little so I would like to know the concrete answer.

Say I have two classes setup as so...

 public class ClassA
 {
     private static ClassA _classA;

     public static ClassA Instance { get { return _classA ?? LoadClassA(); } }

     private ClassA(){}

     public static ClassA LoadClassA()
     {
         _classA = new ClassA();
         return _classA;
     }

     private ClassB _classB = new ClassB();

     public ClassB ClassB { get { return _classB; } set { _classB = value; } }
 }


 public class ClassB
 {
 }

My question is simple.

I'm wondering if the _classB field is treated as static as well if I access the singleton for ClassA? Even though I didn't declare _classB as a static member.

I've always basically just guessed that _classB it is treated as static (one memory location) but I would like to know for sure. Am I wrong? Is a new object created for _classB every time you access it from singleton ClassA...even though there is only one ClassA in memory? Or is it because I newed up _classB on the declaration that causes there to be only one instance of it?

Thanks in advance, -Matt

When you create a singleton, you're creating a single static instance of a non-static type.

In this case, your type (Class A) contains a reference to another type (Class B). The static instance will hold a single reference to a single instance of a Class B object. Technically, it is not "static", but since it's rooted to a static object (the class A instance), it will behave like a static variable. You will always have one and only one Class B object (pointed to by your Class A instance). You will never create more than one Class B instance from within Class A.

There is nothing, however, preventing a second Class B instance to be generated elsewhere - this would be a different instance.

_classB is an instance (not static) member of ClassA. Each instance of ClassA will have one instance of _classB (given the field initializer you've written). So, if you're using the Singleton pattern to access ClassA, and thus always have (at most) one instance of ClassA loaded, you'll always have (at most) one instance of ClassB loaded by way of ClassA.

Now, since ClassB has a public default constructor, something else far away might be creating instances on its own. If that's a concern, consider making ClassB class private. Also, since ClassA has a public default constructor, nothing's stopping folks from creating as many instances as they want. You might make the no-arg constructor private:

private ClassA() {}

The singleton pattern ensures that only one instance of ClassB is accessible from the singleton instance of ClassA at all times. The whole point of the singleton pattern is that it guarantees only one instance of ClassA is available at any time, thus only one reference to _classB (though since ClassA is mutable, this reference can change).

Do however note that the scope of ClassB is still instance-level and not static-level. The compiler will never do anything so strange as to use a different scope specifier than you indicate. You must still access the reference to ClassB via an instance, regardless of whether or not you are using a singleton.

By having your declaration like this:

private ClassB _classB = new ClassB();

You're instantiating _classB to a new instance of ClassB whenever the constructor of ClassA is called.

With a singleton pattern, the only way to call the constructor of ClassA is to use a static method (in your case through the Instance property), which effectively gaurantees that only one ClassA is created.

This ensures that _classB will only be newed up once, but it is non-static. However, if someone changed ClassA to no longer be a singleton in the future, then you would start created multiple instances of ClassB. If _classB were truly static, then this would not be the case.

As defined ClassA violates the definition for singleton. Imagine two threads at the same time call the static Instance property. As the access is not synchronized you could get with two different instances of ClassA and thus two different instances of ClassB.

  1. Thread1 calls Instance property and as _classA is null it enters the LoadClassA method
  2. Thread2 calls Instance property and as _classA is still null it enters the LoadClassA method
  3. Thread1 and Thread2 get two different instances of ClassA

Why not just mark ClassB as static if you only want one instance.

In my opinion it is always better to write clear code that show you full intentions. This way the next person to work on your code doesn't have to play a guessing game to determine what you wanted to do.

Because ClassA is a singleton, there is only one instance of it in your application. Inside that class instance, there is a member to store a ClassB variable. Once you initialize this variable it is an instance variable, but since you only have 1 instance of ClassA, you will always get the same instance of ClassB.

Also I would recommend against your way of creating the internal instance as it is not thread safe.

private static ClassA _instance = new ClassA();

private ClassA() {}

public static ClassA Insance { get { return _instance; } }

by not lazy loading, and initializing right away, you ensure thread safety.

_classB is not static; an instance of ClassA contains a reference to an instance of _classB; if the instance of ClassA is static (as it is in the singleton instance), then it will still contain a reference to _classB, but new instances of ClassA will contain DIFFERENT references to (potentially) different instances of _classB. So if you refer to the same instance of ClassA (through the singleton pattern), you'll still be getting the same instance of _classB; but if you create a new instance of ClassA, you'll get a different instance of _classB, which is why _classB is not static.

I am surprized that no one has pointed out the fact that ClassB is a public property of ClassA with a public setter. As such, nothing stops anybody from writing something like:

ClassA.Instance.ClassB = new ClassB();

This would discard the instance of ClassB created at the time ClassA 's solitary instance was created and would replace it with the newly created instance of ClassB .

A related scenario is that as long as _classB is settable in a derived class (eg, if the setter for ClassB was made protected), there can be no guarantee that there would only be a single instance of ClassB ever.

I suppose the correct way to write this sort of code would be to implement both ClassA and ClassB as singletons and make ClassB an inner class of ClassA (otherwise you would have 2 unrelated classes ClassA and ClassB with a reference to ClassB available through ClassA.Instance.ClassB , which would (in that scenario) be no different from ClassB.Instance .

If ClassB was only instantiable within ClassA , then potentially there could be other instances of ClassB instantiable in a derived class.

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