使用Singleton避免静态变量

pet*_*ski 7 c# singleton static design-patterns

我的一位同事告诉我,我永远不应该使用静态变量,因为如果你在一个地方改变它们,它们就会随处变化.他告诉我,我应该使用Singleton而不是使用静态变量.我知道Singleton用于限制一个类到一个类的实例数.Singleton如何帮助我使用静态变量?

3Da*_*ave 5

一种使用单例的方法(取自http://msdn.microsoft.com/en-us/library/ff650316.aspx)

using System;

public class Singleton
{
   private static Singleton instance;

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (instance == null)
         {
            instance = new Singleton();
         }
         return instance;
      }
   }

   /// non-static members
   public string Foo { get; set; }        

}
Run Code Online (Sandbox Code Playgroud)

然后,

var foo = Singleton.Instance.Foo;
Singleton.Instance.Foo = "Potential thread collision here.";
Run Code Online (Sandbox Code Playgroud)

请注意,实例成员静态字段.你不能在不使用静态变量的情况下实现单例,并且(我似乎记得 - 已经有一段时间了)这个实例将在所有请求中共享.因此,它本身就不是线程安全的.

相反,请考虑将这些值放在数据库或其他更加线程友好的持久性存储中,并创建一个与数据库的该部分接口以提供透明访问的类.

public static class Foo
{
  public static string Bar
  {
    get { /// retrieve Bar from the db }
    set { /// update Bar in the db }
  }
}
Run Code Online (Sandbox Code Playgroud)


Eri*_*ikE 5

让我们一次处理一个语句:

我的一位同事告诉我,我永远不要使用静态变量,因为如果您在一个地方更改它们,它们到处都会被更改。

您的同事似乎很清楚静态变量的基本特征:静态变量只有一个实例。无论您创建任何类的实例有多少,对静态变量的任何访问都将访问同一变量。每个实例没有单独的变量。

他告诉我,应该使用Singleton而不是使用静态变量。

这不是很好的全球建议。静态变量和单例并不相互竞争,也不能真正替代彼此。单例是类的实例,以只能创建一个实例的方式进行管理。静态变量类似地仅与类的一个(静态)实例相关联,但不仅可以分配给类实例,还可以分配给它任何数据类型,例如标量。实际上,要有效使用单例模式,必须将其存储在静态变量中。没有办法“使用单例而不是静态变量”。

另一方面,也许他的意思有些不同:也许他是想说,不是您的静态类具有许多不同的静态变量,方法,属性和字段(总共,成员),它们的作用就像是一个类,而是应该使这些字段为非静态,然后将包装类公开为Singleton实例。您仍然需要一个带有方法或属性的私有静态字段(或者可能仅使用get-only属性)来暴露单例。

我知道Singleton是用于将一类实例的数量限制为一。Singleton如何帮助我解决静态变量?

静态类的变量和单例变量的相似之处在于,它们都必须实例化一次(前者由编译器强制执行,而后者由您的实现强制执行)。您想在类内部使用单例而不是静态变量的原因是,当您的单例需要是一个类的真实实例,而不仅仅是由静态类的静态成员组成时。然后将此单例分配给静态变量,以便所有调用者都可以获取该相同实例的副本。如上所述,您可以将静态类的所有不同静态成员转换为新的非静态类的实例成员,并将其作为单例公开。

我还要提及的是,到目前为止给出的其他答案都与线程安全有关。以下是一些用于管理Singleton的正确模式。

您可以看到Singleton具有实例(或非静态)成员的类的实例是通过静态初始化或在静态构造函数中创建的,并分配给变量_singleton..我们使用此模式来确保它是实例化一次。然后,静态方法Instance提供对后备字段变量的只读访问,该变量包含我们的一个实例,并且只有一个实例Singleton

public class Singleton {
   // static members
   private static readonly Singleton _singleton = new Singleton();
   public static Singleton Instance => _singleton

   // instance members
   private Singleton() { } // private so no one else can accidentally create an instance
   public string Gorp { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

或者,完全相同,但具有显式的静态构造函数:

public class Singleton {
   // static members
   private static readonly Singleton _singleton; // instead of here, you can...
   static Singleton() {
      _singleton = new Singleton(); // do it here
   }
   public static Singleton Instance => _singleton;

   // instance members
   private Singleton() { } // private so no one else can accidentally create an instance
   public string Gorp { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

您还可以使用没有默认后备字段的属性默认值(遵循),或者在静态构造函数中可以分配仅获取属性(未显示)。

public class Singleton {
   // static members
   public static Singleton Instance { get; } = new Singleton();

   // instance members
   private Singleton() { } // private so no one else can accidentally create an instance
   public string Gorp { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

由于保证静态构造函数只运行一次(无论隐式还是显式),因此就没有线程安全问题。请注意,对类的任何访问Singleton都可以触发静态初始化,甚至是反射类型的访问。

您可以将类的静态成员想像成一个单独的(虽然是联结的)类:

  1. 实例(非静态)成员的功能类似于普通类。除非您new Class()对它们表演,否则它们将不存在。每次执行时new,您都会获得一个新实例。实例成员可以访问所有静态成员,包括私有成员(在同一类中)。

  2. 静态成员就像您无法使用明确创建的类的单独的特殊实例的成员new。在此类内,只能访问或设置静态成员。有一个隐式或显式的静态构造函数,.Net会在首次访问时运行(就像类实例一样,只有您没有显式创建它,它是在需要时创建的)。某个类的静态成员可以随时在实例内外访问任何其他类,尽管要遵守诸如internal或的访问修饰符private