C#问题中的singleton模式
本文关键字:singleton 模式 问题 | 更新日期: 2023-09-27 17:58:52
我正在研究C#的单例模式,我在msdn网站上找到了这个例子。
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
private Singleton(){}
public static Singleton Instance
{
get
{
return instance;
}
}
}
因为Singleton实例是由私有静态成员引用变量,实例化不发生,直到类成为第一个由对实例的调用引用所有物因此,该解决方案实现一种形式的懒惰实例化属性,如Singleton的设计模式形式。
我不太确定内存什么时候会分配给
private static readonly Singleton instance
1) 在调用Instance属性时,甚至在调用之前,会发生这种情况吗?
2) 有时我需要强制类创建一个新内存来清除其内容。使用set
这样做安全吗?
set
{
instance = null;
}
在您提供的示例代码中,singleton实例将在首次访问该类时创建。对于您的示例来说,这意味着第一次调用Instance
时。
您可以在Jon Skeet的文章《在C#中实现Singleton模式》中找到更多见解,请参阅方法4。
基本上,为了实现真正的懒惰行为,比如
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
static Singleton(){}
private Singleton(){}
public static Singleton Instance
{
get { return instance; }
}
}
足够了。
(但无论如何,完整且更好的概述可以在上述文章中找到。)
编辑
实际上,正如前面提到的文章中所述,由于BeforeFiledUnit标记,实例不能保证在第一次访问时创建。您需要添加一个空的静态构造函数,这样可以保证它是惰性的。否则,实例将在程序启动和首次访问之间的某个未指定时间创建。(众所周知,.NET运行时2.0有更迫切的策略,所以你可能不会有懒惰的行为。)
引用上述文章:
只有当类型没有用名为
beforefieldinit
的特殊标志标记时,.NET才能保证类型初始化程序的惰性。不幸的是,C#编译器[…]将所有没有静态构造函数[…]的类型标记为beforefieldinit
。
编辑2
如果您希望清理singleton对象,则应该将此功能包含到类Singleton
本身中。
删除属性值将有效地使您的类不再是单例!想象一下,有人访问了singleton并获得了singleton实例。之后,将属性设置为null
。Singleton
类的现有对象不会消失,因为它仍然有引用!因此,下次访问Instance
属性时,将创建Singleton
类的另一个实例。因此,您失去了两件事:您的对象不再是单例(因为您同时存在两个实例),内存也没有被清除。
当类本身被加载时,单例实例将被加载到内存中,也就是当可以调用它的方法开始执行时。调用类的实际行不必实际执行。这是一个很小的区别,但当静态构造函数或静态字段初始值设定项可能引发错误(这里没有)时,可能会产生难以调试的问题。
这在.NET 4中通过新的Lazy<T>
实现得到了修复。
http://msmvps.com/blogs/jon_skeet/archive/2010/01/26/type-initialization-changes-in-net-4-0.aspx
public sealed class Singleton
{
private static readonly Lazy<Singleton> lazy =
new Lazy<Singleton>(() => new Singleton());
public static Singleton Instance { get { return lazy.Value; } }
private Singleton()
{
}
}
http://csharpindepth.com/Articles/General/Singleton.aspx
C#规范说:
10.5.5.1静态场初始化
类的静态字段变量初始值设定项对应于一系列赋值,这些赋值按照它们在类声明中出现的文本顺序执行。如果类中存在静态构造函数(§10.12),则在执行该静态构造函数之前立即执行静态字段初始值设定项。否则,在第一次使用该类的静态字段之前,静态字段初始化程序将在依赖于实现的时间执行。
即,他们只保证在读取instance
字段之前发生。但它可能发生得更早。
如果你想保证它不会在第一次访问属性之前运行,你需要添加一个静态构造函数(可能是空的):
封闭类类型的静态构造函数在给定的应用程序域中最多执行一次。静态构造函数的执行由应用程序域中发生的以下第一个事件触发:
·将创建类类型的实例
·类类型的任何静态成员都被引用
作为侧节点:我注意到,当使用DI时,很少需要使用实际的singleton。简单地告诉DI应该创建一个实例就足够了。如果你稍后决定想要多个实例,这是非常好的,因为它是一个单例的事实并没有融入所有使用它的代码中
实例化直到类首先由调用实例属性
所以。。。无论何时调用.Instance
,或之前的某个时刻。
在首次访问静态成员之前以及在调用静态构造函数(如果有)之前初始化静态成员。
阅读MSDN 上的更多信息