C#的单例模式

我需要存储一堆需要全局访问的变量,我想知道单例模式是否适用。 从我看过的例子来看,单例模式只是一个无法inheritance的静态类。 但我见过的例子对我的需求来说过于复杂。 什么是最简单的单身人士课程? 难道我不能只用一些变量制作一个静态的密封类吗?

通常,单例不是静态类 – 单例将为您提供类的单个实例

我不知道你见过的例子,但通常单例模式在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 registry = new Dictionary(); 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。

您应该看一下dependency injection,因为它通常被认为是最佳实践,但这里要解释的主题太大了:

dependency injection

Singleton不仅仅是一个无法inheritance的静态类。 它是一个常规类,只能实例化一次,每个人都共享该单个实例(并使其线程安全更多的工作)。

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 where T : class { private static readonly Lazy _Lazy = new Lazy(() => { // 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 ,它知道如何使用它的私有构造函数实例化一个类。

它定义了一个Property Instance来访问Lazy字段的Value。

注意传递给Lazy构造函数的LazyThreadSafetyMode枚举。 它正在使用ExecutionAndPublication 。 因此,只允许一个线程初始化Lazy字段的Value。

现在,我们所要做的就是定义将成为单例的包装类:

 public class ExampleSingleton : SingletonBase { 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字段的值,并且我们要断言将从此属性返回的两个实例(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的依赖关系,并且可以使用dependency injection或以下方法之一传递它:

http://houseofbilz.com/archive/2009/05/02.aspx

使用您的语言function。 大多数简单的线程安全实现是:

 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 where T : class { private static readonly Lazy instance = new Lazy( 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 { private MyClass() { //Code... } //Code... } //Example usage of the Singleton class Program { static void Main(string[] args) { MyClass clazz = MyClass.Instance; } }