繁体   English   中英

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

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

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

假设我有两个类设置如此...

 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

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

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

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

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

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

private ClassA() {}

单例模式确保始终只能ClassA的单例实例访问ClassB一个实例。 单例模式的重点在于它保证在任何时候只有一个 ClassA 实例可用,因此只有一个对_classB引用(尽管因为ClassA是可变的,所以这个引用可以改变)。

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

通过这样的声明:

private ClassB _classB = new ClassB();

每当调用ClassA的构造函数时,您都会将_classB实例化为ClassB的新实例。

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

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

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

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

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

在我看来,编写清晰的代码以向您展示完整的意图总是更好。 这样,下一个处理代码的人就不必玩猜谜游戏来确定你想做什么。

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

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

private static ClassA _instance = new ClassA();

private ClassA() {}

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

通过不延迟加载和立即初始化,您可以确保线程安全。

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

令我惊讶的是,没有人指出ClassB是具有公共二传手的ClassA的公共财产。 因此,没有什么能阻止任何人写下这样的东西:

ClassA.Instance.ClassB = new ClassB();

这将丢弃的实例ClassB当时创建ClassA的孤实例被创建,并会用新创建的实例替换它ClassB

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

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

如果ClassB只能在ClassA实例化,那么可能在派生类中可以有其他ClassB实例化实例化。

暂无
暂无

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

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