我的一位同事告诉我,我永远不应该使用静态变量,因为如果你在一个地方改变它们,它们就会随处变化。他告诉我,我应该使用Singleton而不是使用静态变量。 我知道Singleton用于限制一个类到一个类的实例数。 Singleton如何帮助我使用静态变量?
答案 0 :(得分: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; }
}
然后,
var foo = Singleton.Instance.Foo;
Singleton.Instance.Foo = "Potential thread collision here.";
请注意,实例成员是一个静态字段。你不能在不使用静态变量的情况下实现单例,并且(我似乎记得 - 已经有一段时间了)这个实例将在所有请求中共享。因此,它本身就不是线程安全的。
相反,请考虑将这些值放在数据库或其他更加线程友好的持久存储中,并创建一个与数据库的该部分接口的类,以提供透明访问。
public static class Foo
{
public static string Bar
{
get { /// retrieve Bar from the db }
set { /// update Bar in the db }
}
}
答案 1 :(得分:3)
static
修饰符的要点是确保这样修改的对象在任何地方使用都是相同的,因为它不需要实例化。最初作为静态变量出现的名称在内存中具有固定位置,所有引用它的项目都将引用相同的内存位置。
现在您可能希望在类中使用静态字段,在这种情况下,它在实例化(构造)类之前就存在。可能存在您想要这样的情况。
单身人士是一个不同的野兽。它是一个通过使用私有构造函数和static
属性限制为单个实例化的类。所以在这方面你仍然无法通过使用单身来避免静态。
答案 2 :(得分:3)
让我们一次一个地解决你的陈述:
我的一位同事告诉我,我永远不应该使用静态变量,因为如果你在一个地方改变它们,它们会随处变化。
很明显,您的同事意味着静态变量的基本特征:静态变量只有一个实例。无论您创建的任何类的实例数是多少,对静态变量的任何访问都是相同的变量。每个实例都没有单独的变量。
他告诉我,我应该使用Singleton。而不是使用静态变量。
这不是好的全球建议。静态变量和单例彼此不竞争,并不是彼此的真正替代品。单例是一个类的实例,以这样的方式管理,即只能创建一个实例。静态变量类似地绑定到类的一个(静态)实例,但不仅可以分配一个类实例,还可以分配任何数据类型,例如标量。实际上,为了有效地使用单例模式,必须将其存储在静态变量中。没有办法“使用单例而不是静态变量”。
另一方面,也许他的意思略有不同:也许他试图说,而不是你的静态类有许多不同的静态变量,方法,属性和字段(总之,成员),就好像它们是一个在类中,您应该将这些字段设置为非静态,然后将包装类公开为Singleton实例。您仍然需要一个带有方法或属性的私有 static 字段(或者可能只使用get-only属性)来公开单例。
我知道Singleton用于限制一个类的实例数量。 Singleton如何帮助我使用静态变量?
静态类的变量和单例是相似的,因为它们都要实例化一次(前者由编译器强制执行,后者由实现强制执行)。你想要在类中使用单例而不是静态变量的原因是你的单例需要是一个类的真正的实例,而不仅仅是收集的静态成员静态类。然后将此单例分配给静态变量,以便所有调用者都可以获取该同一实例的副本。如上所述,您可以将静态类的所有不同静态成员转换为您将作为单例公开的新非静态类的实例成员。
我还想提一下,到目前为止给出的其他答案都存在线程安全问题。以下是管理单身人士的一些正确模式。
您可以看到具有实例(或非静态)成员的Singleton
类的实例是通过静态初始化或在静态构造函数中创建的,并且被分配给变量{{1我们使用这种模式来确保它只被实例化一次。然后,静态方法_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; }
}
您也可以使用没有显式支持字段的属性默认值(后面跟),或者在静态构造函数中可以指定get-only属性(未显示)。
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; }
}
由于静态构造函数保证只运行一次,无论是隐式还是显式,因此没有线程安全问题。请注意,对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; }
}
类的任何访问都可以触发静态初始化,甚至是反射类型访问。
你可以认为类的静态成员几乎就像一个单独的,但是连在一起的类:
实例(非静态)成员的功能类似于普通类。在您对它们执行Singleton
之前,它们不会生效。每次执行new Class()
时,都会获得一个新实例。实例成员可以访问所有静态成员,包括私有成员(在同一个类中)。
静态成员类似于您无法使用new
显式创建的类的单独特殊实例的成员。在此类中,只能访问或设置静态成员。有一个隐式或显式静态构造函数.Net在第一次访问时运行(就像类实例一样,只有你没有显式创建它,它是在需要时创建的)。任何其他类可以在实例内外访问类的静态成员,但要尊重new
或internal
等访问修饰符。
答案 3 :(得分:1)
回答上述问题:
创建一个没有静态字段的单例是非常愚蠢的(但可能)。
为此,您需要使用其他人的静态字段,例如AppDomain.GetData
或(在ASP.Net中)HttpContext.Application
。
答案 4 :(得分:0)
回答你的问题(我希望):不要使用包含静态成员(如Class1)的静态类,而是像Class2一样实现Singleton模式(请不要在此时开始讨论延迟初始化):< / p>
public static class Class1
{
public static void DoSomething ()
{
}
}
public static class Class2
{
private Class2() {
}
private Class2 instance;
public Class2 GetInstance(){
if (instance == null)
instance = new Class2();
return instance;
}
public void DoSomething ()
{
}
}
您可以使用Class1.DoSomething()
。
Class2.GetInstance().DoSomething()
编辑:正如您所看到的,Class2中仍然有一个(私有)静态字段,用于保存它的实例。
Edit2回答user966638的评论: 我理解你是否更正,你有这样的代码:
public class Foo {
private static Bar bar;
}
你的同事建议用这个代替吗?
public class Foo {
private BarSingleton bar;
}
如果您想要使用不同的Foo实例,例如每个实例的bar属性可以设置为null,则可能出现这种情况。但我不确定他是否意味着他正在谈论的用例究竟是什么。
答案 5 :(得分:-2)
单例和静态变量都为您提供了一个类的实例。为什么你应该更喜欢单身而不是静态呢
希望这有帮助