我有一个关于单身人士的问题,我认为我知道答案......但每次情况弹出时我都会稍微猜测一下自己,所以我想知道具体的答案。
说我设置了两个类......
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
答案 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实例。
答案 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)
我很惊讶,没有人指出ClassB
是ClassA
公共财产的公共财产的事实。因此,没有什么能阻止任何人写下这样的东西:
ClassA.Instance.ClassB = new ClassB();
这将丢弃在ClassB
创建单独实例时创建的ClassA
实例,并将其替换为新创建的ClassB
实例。
相关场景是,只要_classB
在派生类中可设置(例如,如果ClassB
的setter被保护),就不能保证只有ClassB
ClassA
的单个实例。
我认为编写此类代码的正确方法是将ClassB
和ClassB
实现为单例,并使ClassA
成为ClassA
的内部类(否则)您将有2个不相关的类ClassB
和ClassB
,其中ClassA.Instance.ClassB
可通过ClassB.Instance
提供,这将(在此方案中)与ClassB
没有区别
如果ClassA
仅在ClassB
内可实例化,则可能在派生类中可能存在其他{{1}}实例化实例。