简体   繁体   English

关于单例的非静态成员的问题(C#)

[英]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? 我想知道如果我访问ClassA的单例,_classB字段是否也被视为静态? Even though I didn't declare _classB as a static member. 即使我没有将_classB声明为静态成员。

I've always basically just guessed that _classB it is treated as static (one memory location) but I would like to know for sure. 我总是基本上猜到_classB它被视为静态(一个内存位置),但我想知道肯定。 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? 每次从singleton ClassA访问时都会为_classB创建一个新对象...即使内存中只有一个ClassA? Or is it because I newed up _classB on the declaration that causes there to be only one instance of it? 或者是因为我在声明中新建了_classB,导致只有一个实例?

Thanks in advance, -Matt 提前谢谢,-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). 在这种情况下,您的类型(A类)包含对另一种类型(B类)的引用。 The static instance will hold a single reference to a single instance of a Class B object. 静态实例将保存对B类对象的单个实例的单个引用。 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. 从技术上讲,它不是“静态的”,但由于它的根源是静态对象(类A实例),它的行为就像一个静态变量。 You will always have one and only one Class B object (pointed to by your Class A instance). 您将始终拥有一个且仅有一个B类对象(由A类实例指向)。 You will never create more than one Class B instance from within Class A. 您永远不会在A类中创建多个B类实例

There is nothing, however, preventing a second Class B instance to be generated elsewhere - this would be a different instance. 但是,没有什么能阻止在其他地方生成第二个B类实例 - 这将是一个不同的实例。

_classB is an instance (not static) member of ClassA. _classB是ClassA的实例(非静态)成员。 Each instance of ClassA will have one instance of _classB (given the field initializer you've written). ClassA的每个实例都有一个_classB实例(给定你编写的字段初始值设定项)。 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. 因此,如果您使用Singleton模式访问ClassA,因此总是(最多)加载一个ClassA实例,您将始终(最多)通过ClassA加载一个ClassB实例。

Now, since ClassB has a public default constructor, something else far away might be creating instances on its own. 现在,由于ClassB有一个公共默认构造函数,所以远在其他地方可能会自己创建实例。 If that's a concern, consider making ClassB class private. 如果这是一个问题,请考虑将ClassB类设为私有。 Also, since ClassA has a public default constructor, nothing's stopping folks from creating as many instances as they want. 此外,由于ClassA具有公共默认构造函数,因此没有什么能阻止人们创建任意数量的实例。 You might make the no-arg constructor private: 您可以将no-arg构造函数设为私有:

private ClassA() {}

The singleton pattern ensures that only one instance of ClassB is accessible from the singleton instance of ClassA at all times. 单例模式确保始终只能ClassA的单例实例访问ClassB一个实例。 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). 单例模式的重点在于它保证在任何时候只有一个 ClassA 实例可用,因此只有一个对_classB引用(尽管因为ClassA是可变的,所以这个引用可以改变)。

Do however note that the scope of ClassB is still instance-level and not static-level. 但请注意, ClassB范围仍然是实例级而不是静态级。 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. 无论您是否使用单例,您仍必须通过实例访问ClassB的引用。

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. 每当调用ClassA的构造函数时,您都会将_classB实例化为ClassB的新实例。

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. 使用单例模式,调用ClassA的构造函数的唯一方法是使用静态方法(在您的情况下通过Instance属性),这有效地保证只创建一个ClassA。

This ensures that _classB will only be newed up once, but it is non-static. 这样可以确保_classB只会被新建一次,但它是非静态的。 However, if someone changed ClassA to no longer be a singleton in the future, then you would start created multiple instances of ClassB. 但是,如果有人将ClassA更改为将来不再是单例,那么您将开始创建多个ClassB实例。 If _classB were truly static, then this would not be the case. 如果_classB真的是静态的,那么情况就不是这样了。

As defined ClassA violates the definition for singleton. 如定义的,ClassA违反了单身人士的定义。 Imagine two threads at the same time call the static Instance property. 想象一下两个线程同时调用静态Instance属性。 As the access is not synchronized you could get with two different instances of ClassA and thus two different instances of ClassB. 由于访问不同步,您可以使用两个不同的ClassA实例,从而获得两个不同的ClassB实例。

  1. Thread1 calls Instance property and as _classA is null it enters the LoadClassA method Thread1调用Instance属性,当_classA为null时,它进入LoadClassA方法
  2. Thread2 calls Instance property and as _classA is still null it enters the LoadClassA method Thread2调用Instance属性,因为_classA仍为null,它进入LoadClassA方法
  3. Thread1 and Thread2 get two different instances of ClassA Thread1和Thread2获得ClassA的两个不同实例

Why not just mark ClassB as static if you only want one instance. 如果只需要一个实例,为什么不将ClassB标记为静态。

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. 因为ClassA是单例,所以在您的应用程序中只有一个实例。 Inside that class instance, there is a member to store a ClassB variable. 在该类实例中,有一个存储ClassB变量的成员。 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. 初始化此变量后,它是一个实例变量,但由于您只有1个ClassA实例,因此您将始终获得相同的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; _classB不是静态的; an instance of ClassA contains a reference to an instance of _classB; ClassA的实例包含对_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. 如果ClassA的实例是静态的(因为它在单例实例中),那么它仍将包含对_classB的引用,但ClassA的新实例将包含对(可能)_classB的不同实例的DIFFERENT引用。 So if you refer to the same instance of ClassA (through the singleton pattern), you'll still be getting the same instance of _classB; 因此,如果您引用相同的ClassA实例(通过单例模式),您仍将获得相同的_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. 但是如果你创建一个新的ClassA实例,你会得到一个不同的_classB实例,这就是为什么_classB不是静态的。

I am surprized that no one has pointed out the fact that ClassB is a public property of ClassA with a public setter. 令我惊讶的是,没有人指出ClassB是具有公共二传手的ClassA的公共财产。 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 . 这将丢弃的实例ClassB当时创建ClassA的孤实例被创建,并会用新创建的实例替换它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. 一个相关的场景是,只要_classB在派生类中是可设置的(例如,如果ClassB的setter受到保护),就不能保证只有ClassB的单个实例。

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 . 我想编写这种代码的正确方法是将ClassAClassB都实现为单例,并使ClassB成为ClassA的内部类(否则你将有2个不相关的类ClassAClassB ,通过ClassA.Instance.ClassB可以引用ClassB ClassA.Instance.ClassB ,它(在那种情况下)与ClassB.Instance没有什么不同。

If ClassB was only instantiable within ClassA , then potentially there could be other instances of ClassB instantiable in a derived class. 如果ClassB只能在ClassA实例化,那么可能在派生类中可以有其他ClassB实例化实例化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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