C#的单例模式[关闭]
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#的单例模式[关闭]相关的知识,希望对你有一定的参考价值。
我需要存储一堆需要全局访问的变量,我想知道单例模式是否适用。从我看过的例子来看,单例模式只是一个无法继承的静态类。但我见过的例子对我的需求来说过于复杂。什么是最简单的单身人士课程?难道我不能只用一些变量制作一个静态的密封类吗?
通常,单例不是静态类 - 单例将为您提供类的单个实例。
我不知道你见过的例子,但通常singleton pattern在C#中非常简单:
public sealed class Singleton
{
private static readonly Singleton instance = new Singleton();
static Singleton() {} // Make sure it's truly lazy
private Singleton() {} // Prevent instantiation outside
public static Singleton Instance { get { return instance; } }
}
这并不困难。
单例相对于静态成员的优点是类可以实现接口等。有时这很有用 - 但有时候,静态成员确实也会这样做。另外,以后通常更容易从单身人士转移到非单身人士,例如将单例作为“配置”对象传递给依赖类,而不是那些进行直接静态调用的依赖类。
就个人而言,我会尽量避免使用单身人士 - 除了其他任何事情之外,他们会更加努力地进行测试。它们偶尔会有用。
有几种模式可能适合你,单身是最糟糕的模式之一。
注册处
struct Data {
public String ProgramName;
public String Parameters;
}
class FooRegistry {
private static Dictionary<String, Data> registry = new Dictionary<String, Data>();
public static void Register(String key, Data data) {
FooRegistry.registry[key] = data;
}
public static void Get(String key) {
// Omitted: Check if key exists
return FooRegistry.registry[key];
}
}
好处
- 易于切换到Mock对象进行自动化测试
- 您仍然可以存储多个实例,但如果需要,您只有一个实例。
缺点
- 比Singleton或全局变量略慢
静态类
class GlobalStuff {
public static String ProgramName {get;set;}
public static String Parameters {get;set;}
private GlobalStuff() {}
}
好处
- 简单
- 快速
缺点
- 很难动态切换到模拟对象
- 如果需求发生变化,很难切换到另一种对象类型
简单的单身人士
class DataSingleton {
private static DataSingleton instance = null;
private DataSingleton() {}
public static DataSingleton Instance {
get {
if (DataSingleton.instance == null) DataSingleton.instance = new DataSingleton();
return DataSingleton;
}
}
}
好处
- 没有
缺点
- 难以创建线程安全单例,如果多个线程访问实例,上述版本将失败。
- 很难切换模拟对象
我个人喜欢注册表模式,但YMMV。
您应该看一下依赖注入,因为它通常被认为是最佳实践,但这里解释的主题太大了:
Singleton不仅仅是一个无法继承的静态类。它是一个常规类,只能实例化一次,每个人都共享该单个实例(并使其线程安全更多的工作)。
Singleton的典型.NET代码如下所示。这是一个快速示例,绝不是最佳实现或线程安全代码:
public sealed class Singleton
{
Singleton _instance = null;
public Singleton Instance
{
get
{
if(_instance == null)
_instance = new Singleton();
return _instance;
}
}
// Default private constructor so only we can instanctiate
private Singleton() { }
// Default private static constructor
private static Singleton() { }
}
如果你要沿着你想要的道路走下去,一个静态密封的课程就可以了。
使用C#6自动属性初始化程序。
public sealed class Singleton
{
private Singleton() { }
public static Singleton Instance { get; } = new Singleton();
}
简短而干净 - 我会很高兴听到缺点。
我知道这个问题已经过时了,但这是另一个使用.Net 4.0或更高版本的解决方案(包括.Net Core和.Net Standard)。
首先,定义将转换为Singleton的类:
public class ClassThatWillBeASingleton
{
private ClassThatWillBeASingleton()
{
Thread.Sleep(20);
guid = Guid.NewGuid();
Thread.Sleep(20);
}
public Guid guid { get; set; }
}
在这个示例类中,我定义了一个睡眠一段时间的构造函数,然后创建一个新的Guid并保存到它的公共属性。 (Sleep只适用于并发测试)
请注意,构造函数是私有的,因此没有人可以创建此类的新实例。
现在,我们需要定义将此类转换为单例的包装器:
public abstract class SingletonBase<T> where T : class
{
private static readonly Lazy<T> _Lazy = new Lazy<T>(() =>
{
// Get non-public constructors for T.
var ctors = typeof(T).GetConstructors(System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic);
if (!Array.Exists(ctors, (ci) => ci.GetParameters().Length == 0))
throw new InvalidOperationException("Non-public ctor() was not found.");
var ctor = Array.Find(ctors, (ci) => ci.GetParameters().Length == 0);
// Invoke constructor and return resulting object.
return ctor.Invoke(new object[] { }) as T;
}, System.Threading.LazyThreadSafetyMode.ExecutionAndPublication);
public static T Instance
{
get { return _Lazy.Value; }
}
}
请注意,它使用Lazy创建一个字段_Lazy
,它知道如何使用它的私有构造函数实例化一个类。
它定义了一个属性Instance
来访问Lazy字段的值。
注意传递给Lazy构造函数的LazyThreadSafetyMode
枚举。它使用ExecutionAndPublication
。因此,只允许一个线程初始化Lazy字段的Value。
现在,我们要做的就是定义将成为单例的包装类:
public class ExampleSingleton : SingletonBase<ClassThatWillBeASingleton>
{
private ExampleSingleton () { }
}
以下是一个用法示例:
ExampleSingleton.Instance.guid;
并且一个测试断言两个线程将获得Singleton的相同实例:
[Fact()]
public void Instance_ParallelGuid_ExpectedReturnSameGuid()
{
Guid firstGuid = Guid.Empty;
Guid secondGuid = Guid.NewGuid();
Parallel.Invoke(() =>
{
firstGuid = Singleton4Tests.Instance.guid;
}, () =>
{
secondGuid = Singleton4Tests.Instance.guid;
});
Assert.Equal(firstGuid, secondGuid);
}
此测试同时调用Lazy字段的值,并且我们要声明将从此属性(Value of Lazy)返回的两个实例都相同。
有关此主题的更多详细信息,请访问:C# in Depth
所以,就我而言,这是C#中Singleton模式最简洁,最简单的实现。
http://blueonionsoftware.com/blog.aspx?p=c6e72c38-2839-4696-990a-3fbf9b2b0ba4
然而,我会建议单身人士是非常丑陋的模式......我认为它们是一种反模式。
http://blogs.msdn.com/scottdensmore/archive/2004/05/25/140827.aspx
对我来说,我更喜欢像Repository那样实现IRepository。您的类可以在构造函数中声明对IRepository的依赖,并且可以使用依赖注入或以下方法之一传递它:
http://houseofbilz.com/archive/2009/05/02.aspx
使用您的语言功能。大多数简单的线程安全实现是:
public sealed class Singleton
{
private static readonly Singleton _instance;
private Singleton() { }
static Singleton()
{
_instance = new Singleton();
}
public static Singleton Instance
{
get { return _instance; }
}
}
......最简单的单身人士课程是什么?
只是添加一个可能的解决方案。我能想到的最简单,最直接和易用的方法是这样的:
//The abstract singleton
public abstract class Singleton<T> where T : class
{
private static readonly Lazy<T> instance = new Lazy<T>( CreateInstance, true );
public static T Instance => instance.Value;
private static T CreateInstance()
{
return (T)Activator.CreateInstance( typeof(T), true);
}
}
//This is the usage for any class, that should be a singleton
public class MyClass : Singleton<MyClass>
{
private MyClass()
{
//Code...
}
//Code...
}
//Example usage of the Singleton
class Program
{
static void Main(string[] args)
{
MyClass clazz = MyClass.Instance;
}
}
以上是关于C#的单例模式[关闭]的主要内容,如果未能解决你的问题,请参考以下文章