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

时间:2009-07-06 17:43:50

标签: c# design-patterns static singleton

我有一个关于单身人士的问题,我认为我知道答案......但每次情况弹出时我都会稍微猜测一下自己,所以我想知道具体的答案。

说我设置了两个类......

 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
 {
 }

我的问题很简单。

我想知道如果我访问ClassA的单例,_classB字段是否也被视为静态?即使我没有将_classB声明为静态成员。

我总是基本上猜测_classB它被视为静态(一个内存位置),但我想知道肯定。我错了吗?每次从singleton ClassA访问时都会为_classB创建一个新对象...即使内存中只有一个ClassA?或者是因为我在声明上新建了_classB,导致只有一个实例?

提前致谢, -Matt

9 个答案:

答案 0 :(得分:11)

创建单例时,您将创建一个非静态类型的静态实例。

在这种情况下,您的类型(A类)包含对另一种类型(B类)的引用。静态实例将保存对B类对象的单个实例的单个引用。从技术上讲,它不是“静态的”,但由于它的根源是静态对象(类A实例),它的行为就像一个静态变量。您将始终拥有一个且仅有一个B类对象(由A类实例指向)。您永远不会在A类中创建多个B类实例

然而,没有什么可以阻止在其他地方生成第二个B类实例 - 这将是一个不同的实例。

答案 1 :(得分:3)

_classB是ClassA的实例(非静态)成员。 ClassA的每个实例都有一个_classB实例(给定你编写的字段初始值设定项)。因此,如果您使用Singleton模式访问ClassA,因此总是(最多)加载一个ClassA实例,您将始终(最多)通过ClassA加载一个ClassB实例。

现在,由于ClassB有一个公共默认构造函数,所以远在其他地方可能会自己创建实例。如果这是一个问题,请考虑将ClassB类设为私有。此外,由于ClassA具有公共默认构造函数,因此没有什么能阻止人们创建任意数量的实例。您可以将no-arg构造函数设为私有:

private ClassA() {}

答案 2 :(得分:2)

单例模式确保始终只能从ClassB 的单例实例访问ClassA个实例。单例模式的重点在于它保证只有ClassA的一个实例随时可用,因此只有一个_classB的引用(尽管从{{1}开始) }是可变的,这个引用可以改变。)

但是请注意ClassA范围仍然是实例级别而不是静态级别。编译器将从不做任何奇怪的事情,以至于使用与您指示的不同的范围说明符。无论您是否使用单例,您仍必须通过实例访问ClassB的引用。

答案 3 :(得分:2)

通过这样的声明:

private ClassB _classB = new ClassB();

只要调用ClassA的构造函数,就会将_classB实例化为ClassB的新实例。

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

这确保_classB只会被新建一次,但它是非静态的。但是,如果有人将ClassA更改为将来不再是单例,那么您将开始创建多个ClassB实例。如果_classB真的是静态的,那么情况并非如此。

答案 4 :(得分:1)

如定义的,ClassA违反了单身人士的定义。想象一下两个线程同时调用静态Instance属性。由于访问不同步,您可以使用两个不同的ClassA实例,从而获得两个不同的ClassB实例。

  1. Thread1调用Instance属性,而_classA为null,它进入LoadClassA方法
  2. Thread2调用Instance属性,因为_classA仍为null,所以它进入LoadClassA方法
  3. Thread1和Thread2获得两个不同的ClassA
  4. 实例

答案 5 :(得分:0)

如果您只需要一个实例,为什么不将ClassB标记为静态。

在我看来,编写清晰的代码可以更好地向您展示完整的意图。通过这种方式,下一个处理代码的人不必玩猜谜游戏来确定你想要做什么。

答案 6 :(得分:0)

因为ClassA是一个单例,所以在你的应用程序中只有一个实例。在该类实例中,有一个存储ClassB变量的成员。初始化此变量后,它是一个实例变量,但由于您只有1个ClassA实例,因此您将始终获得相同的ClassB实例。

另外,我建议您不要创建内部实例,因为它不是线程安全的。

private static ClassA _instance = new ClassA();

private ClassA() {}

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

不延迟加载,并立即初始化,确保线程安全。

答案 7 :(得分:0)

_classB不是静态的; ClassA的实例包含对_classB实例的引用;如果ClassA的实例是静态的(因为它在单例实例中),那么它仍将包含对_classB的引用,但ClassA的新实例将包含对(可能)_classB的不同实例的DIFFERENT引用。因此,如果您引用相同的ClassA实例(通过单例模式),您仍将获得相同的_classB实例;但是如果你创建一个新的ClassA实例,你会得到一个不同的_classB实例,这就是为什么_classB不是静态的。

答案 8 :(得分:0)

我很惊讶,没有人指出ClassBClassA公共财产的公共财产的事实。因此,没有什么能阻止任何人写下这样的东西:

ClassA.Instance.ClassB = new ClassB();

这将丢弃在ClassB创建单独实例时创建的ClassA实例,并将其替换为新创建的ClassB实例。

相关场景是,只要_classB在派生类中可设置(例如,如果ClassB的setter被保护),就不能保证只有ClassB ClassA的单个实例。

我认为编写此类代码的正确方法是将ClassBClassB实现为单例,并使ClassA成为ClassA的内部类(否则)您将有2个不相关的类ClassBClassB,其中ClassA.Instance.ClassB可通过ClassB.Instance提供,这将(在此方案中)与ClassB没有区别

如果ClassA仅在ClassB内可实例化,则可能在派生类中可能存在其他{{1}}实例化实例。