我知道关于这个问题还有其他线索,但我找不到令人满意的答案。为什么单例类需要用C密封?(澄清我的问题)把它密封真的有助于它实现单件(只有一个例子)的目标吗?
我增加了更多关于我想知道什么的澄清,并试图找出原因,希望读者理解我想讨论的内容。
我正在谈论的是msdn文章的线程安全解决方案:https://msdn.microsoft.com/en-us/library/ff650316.aspx.
在本文中,他们首先给出了非线程安全的解决方案,然后给出了另一个使用静态初始化使其线程安全的解决方案,在该解决方案中,他们使类:sealed
根据msdn的解释——类被标记为密封的以防止派生,这可能会添加实例。有关标记密封类的优缺点的讨论,请参阅[sells03]。
当我试图弄清楚为什么它们在静态初始化方法中使用sealed时,我唯一能想到的是:由于它们想要防止附加的实例化,所以它们将其密封(这只防止通过派生进行实例化,嵌套的子类派生可以创建附加的实例,但通过使用sealed,它不会阻止嵌套的cl通过创建额外的实例来实现singleton的目标。如果使用sealed不能防止通过其他方式进行额外的实例化,那么为什么要使用它呢?
以下是msdn的线程安全解决方案:
公共密封类单件{private static readonly singleton instance=new singleton();
private singleton()
公共静态单实例{得到{返回实例;}}}
我的问题是,msdn的线程安全解决方案是否通过密封实现了singleton的目标?他们将类密封起来以防止通过派生进行实例化,但这并不能阻止通过其他方式进行的实例化。请参阅下面的示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public sealed class Singleton
{
private static readonly Singleton instance = new Singleton ();
private Singleton () { }
public static Singleton Instance
{
get
{
return instance ;
}
}
public class nestedSingleton
{
public nestedSingleton ()
{
Singleton moreThanOneObject = new Singleton ();
}
}
} |
简单地说:如果有人可以从中派生,那么可以有多个实例。因此,您肯定需要某种方法来防止任意派生。
现在,您可以简单地依赖一个私有构造函数来防止子类化(无论如何,您仍然需要私有构造函数),但是使Singleton类未密封的唯一原因是,如果您希望嵌套类型是实现,例如。
1 2 3 4 5 6 7 8 9 10 11
| public class Singleton
{
public static Singleton Instance { get; }
= (DateTime .Now.Seconds & 1) == 0
? (Singleton ) new EvenSingleton () : new OddSingleton ();
private Singleton () {}
private class OddSingleton : Singleton {}
private class EvenSingleton : Singleton {}
} |
这确实是一个单身汉-但这是一个相当不寻常的情况。更常见的是不需要一个嵌套类型来实现,在这一点上,更清楚的是imo要密封singleton以使它更清楚地表明您不打算存在任何子类。
基本上,它与任何东西都是通信的——sealed类显然禁止派生,而只有私有构造函数(但没有嵌套类型)使其更加隐式。我认为直白更清楚。
(同样,在C的早期版本中,有一些奇怪的方法可以让子类通过相互递归的构造函数调用进行编译,这样就永远不会得到基本构造函数初始值设定项。幸运的是,这个问题现在已经解决了。)
回答问题添加:
Sealed keyword doesnt even make sense in this case if MSDN added it to prevent creating more instances of singleton by Nested sub classes.
在您给出的示例中,嵌套类不是子类。看起来是这样的:
1
| public class nestedSingleton |
而嵌套的子类如下所示:
1
| public class nestedSingleton : Singleton |
(除非理想情况下遵循.NET命名约定)。
- 我认为问题的最后一段清楚地表明,我们无论如何都会有一个private构造函数,这已经阻止了这一点。在我看来,这只是一个问题,就是MSDN文档有点混乱。
- @乔恩:坦率地说,我觉得这个问题有点难解析,但是的,它是可行的——我已经更新了答案。
- 奥德辛格尔顿和伊文辛格尔顿真的很奇怪:)
- @乔恩,这不回答我的问题。我在我的问题中增加了更多的澄清和细节。
- @学习者:我认为它确实回答了这个问题:不,这个类不需要被密封,但是如果不需要嵌套的子类,那么密封它有助于传达这样一个事实,即你不希望任何人试图从中派生。注意,您示例中的嵌套类不是Singleton的子类,所以可以密封Singleton。
- @学习者:我也建议你多编辑一下这个问题。代码的格式都不正确,不清楚从何处获取嵌套类示例,因为它不是来自您所引用的msdn页面。
- 我对我的要求做了更多的解释和澄清。我知道我的嵌套类:公共类nested singleton不是从singleton继承的,因为我想证明在这种情况下,如果msdn声称将其密封将阻止进一步的实例化,那么sealed就没有意义。当然,sealed会阻止创建嵌套的子类,从而不让它们创建singleton的实例,但是,如果嵌套类根本不是从singleton派生的,而是仍然创建singleton的实例,那该怎么办呢?
- @学习者:这必须在Singleton的程序文本中,因此被认为是可信的。如果知道嵌套类不是子类,为什么说它是子类?那太令人困惑了。现在,sealed并不阻止实例化——它只阻止派生。但是,它清楚地传达了类不打算从中派生的信息,而私有构造函数则使其不那么明显。我不明白你为什么认为msdn是在谈论嵌套类,尽管"嵌套"这个词没有出现。
- @学习者:另外,你的问题仍然格式不好。请阅读stackoverflow.com/editing-help
- @Jon Skeet感谢您的回复,我问的是msdn说他们使用了sealed来防止实例化(他们没有说他们引用的是什么类型的实例化,所以我假设他们引用的是通过嵌套子类完成的实例化,因为非嵌套类根本不能从singleton继承。这就是我的猜测,msdn使用sealed来防止通过嵌套的子类进行实例化,因为这是唯一的其他选项,sealed会阻止它们,但我的问题是sealed只会阻止嵌套的子类,但是嵌套的类仍然可以创建更多的实例)
- @学习者:不是这样说的。它说"类被标记为sealed以防止派生,从而可以添加实例"。派生!=实例化。如果有任何派生类,它们可以有公共构造函数…
- @乔恩·斯基特,我理解推导!=实例化,但防止派生的目的是通过嵌套的子类(这是派生的唯一可能方法)防止实例化(不超过一个singleton实例)。该解决方案(使其密封)是否阻止通过嵌套类创建更多实例?如果使其密封并不能解决以所有方式阻止进一步实例化的问题,那么为什么要使类密封(只防止通过嵌套的子类进行实例化),因为我们仍然可以通过嵌套的类创建实例?
- @学习者:恐怕我想我已经尽可能多地回答了这个问题。注意,msdn在任何地方都没有提到嵌套类,所以我认为本文作者根本没有考虑到它们。
不一定是
在C中实现单例#
这在多线程上不安全,而在单线程上安全
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Singleton
{
private static Singleton instance ;
private Singleton () {}
public static Singleton Instance
{
get
{
if (instance == null)
{
instance = new Singleton ();
}
return instance ;
}
}
} |
将其密封将使其不可能继承(消除了单个实例的几个可能原因)
- 但这当然有一个种族条件…
- 确切地。问题是,密封只阻止类的派生。
- 它不需要密封。但将构造函数标记为private本质上等于sealed。
- 问题的关键是,为什么要密封它?嗯,不一定是这样。所有的操作需要做的是阅读密封和它做什么。(ii'假设他有,那么下一个问题是,它不一定是,但有陷阱与之)这就是为什么有链接到MS设计和模式,详细说明为什么密封在使用中。
- @托马斯,这不回答我的问题。我在我的问题中增加了更多的澄清和细节。我已经在讨论来自msdn文章的线程安全解决方案
原因与实现细节密切相关。好的。
例如,在C中看到以下简单和错误的单例实现:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class A
{
private static readonly A _instance ;
static A ()
{
_instance = new A ();
}
public static A Instance => _instance ;
public string Name { get; set; }
} |
现在让我们把它推到B中:好的。
1 2 3 4
| public class B : A
{
public string Surname { get; set; }
} |
如果您调用B.Current静态属性会发生什么?好的。
1 2
| // ERROR! Surname isn't a property of A
string surname = B.Instance.Surname; |
不密封单例类的一个主要缺点是静态成员仍然可以在派生类上访问。也就是说,您可以访问B上的Instance属性,但它返回A的单实例,这非常令人困惑。好的。
你不能冒这个风险,一个简单而有效的避免混淆的方法就是密封A。由于A是单例实现,所以您不希望继承,因为派生类不能扩展单例。好的。
如果您同意避免所描述的情况,那么更方便地实现singleton可以是:好的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| // SEALED
public sealed class A
{
private static readonly A _instance ;
// Avoid that other code excepting A class members
// can instantiate A
private A () {}
static A ()
{
_instance = new A ();
}
public static A Instance => _instance ;
public string Name { get; set; }
} |
关于使用静态只读字段而不是延迟加载单个实例的批评…
我还检查了是否有人反对我将单个实例实例化为静态构造函数的一部分或使用字段初始值设定项的建议。我还可以将实现简化如下:好的。
1 2 3 4 5 6 7 8 9
| public sealed class A
{
private A () {}
private static readonly A _instance = new A ();
public static A Instance => _instance ;
public string Name { get; set; }
} |
据我所知,它比在Instance静态属性getter中实例化单个A实例提供更多的优势:好的。
…好的。
1 2 3 4 5 6 7 8 9 10 11
| private static A _instance ;
public static A Instance
{
get
{
if(_instance == null) _instance = new A ();
return _instance ;
}
} |
请参阅另一个问题A,了解静态构造函数是线程安全的:C静态构造函数是线程安全的吗?好的。回应我对我的示例单例实现的批评
在一些评论中,乔恩·斯基特说:好的。
A singleton doesn't just provide a single instance - it ensure that
it's the only instance ever created. See
en.wikipedia.org/wiki/Singleton_pattern"In software engineering, the
singleton pattern is a design pattern that restricts the instantiation
of a class to one object." Your code doesn't do that.
Ok.
这不是使我的示例实现无效的好理由。例如,大多数反向控制容器允许您将给定组件及其实现的生命周期配置为单例。好的。
整个实现是一个常规的公共类,没有限制,甚至无法为其他组件配置相同的实现,或者您甚至可以在任何需要的地方用new实例化实现(即new A())。好的。
但所谓的组件是单例的,因为您已经配置了它将是整个AppDomain中整个组件的单个实例。好的。
现在,让我们看看关于单例模式(wikipedia)的实现部分:好的。
Implementation of a singleton pattern must satisfy the single instance
and global access principles. It requires a mechanism to access the
singleton class member without creating a class object and a mechanism
to persist the value of class members among class objects. The
singleton pattern is implemented by creating a class with a method
that creates a new instance of the class if one does not exist. If an
instance already exists, it simply returns a reference to that object.
To make sure that the object cannot be instantiated any other way, the
constructor is made private. Note the distinction between a simple
static instance of a class and a singleton: although a singleton can
be implemented as a static instance, it can also be lazily
constructed, requiring no memory or resources until needed.
Ok.
将此实现描述与我的示例实现进行比较,我认为以下实现是单例实现:好的。
1 2 3 4 5 6 7 8 9
| public sealed class A
{
private A () {}
private static readonly A _instance = new A ();
public static A Instance => _instance ;
public string Name { get; set; }
} |
你不能公开实例化它。
它只能是给定AppDomain中的单个实例。
它提供了对自身的全局访问。
它实现了一种创建单个实例的方法,但它使用的是特定于C的语法和语法糖。
当静态Instance属性第一次被实例化时,它被延迟地实例化。这就是静态构造函数和静态类字段初始值设定项在C中的工作方式。
现在让我们分析一下控制容器的反转情况:好的。
通常组件由接口和抽象类定义。很少有具体的课程。因此,不能实例化抽象类或接口。
没有Instance静态属性,但是wikipedia上的实现描述说:…]它需要一个机制来访问singleton类成员,而不创建类对象,还需要一个机制来保持类成员在类对象之间的值。您可以使用singleton类的Instance静态属性来实现这一点,这只是一些编程语言的实现细节。模式描述可以很好地解释控制容器的反转是如何工作的。或者像服务定位器反模式或者仅仅依赖注入,都是提供单个实例的机制。
通过反转控制容器,您可以全局访问单例。例如,Container.Resolve()。顺便说一句,除了少数情况外,服务定位器被认为是一种反模式。
大多数反向控制容器都可以提供自定义工厂,您可以在其中定义如何实例化组件实现。
基于前面的观点,您可以很容易地实现工厂的延迟加载。
总之,我想说的是,在我的特定案例中,我已经离开了我的Ortoxy,我假设设计模式是抽象的定义,用于实现常见问题的解决方案,大多数都是并且应该是语言不可知论者。只要我的代码对给定的设计模式进行描述,就足以考虑我正在实现整个设计模式。甚至可以在不调用类whateversingleton的情况下实现singleton。好的。好啊。
- 例如,在C中看到以下简单的单例实现:这不是单例实现。您的类只是公开一个静态实例。
- 我不明白这一切与单身有什么关系。当然,您必须假设私有的构造函数(这将阻止继承),否则它就不是一个单一的想象。
- @sriramsakthivel静态构造函数是初始化静态类字段的好方法,不是吗?如果您需要确保给定的AppDomain中只有一个实例,那么当我使用静态构造函数并设置readonly字段时,您需要让我相信我是错的…
- @乔恩,我讨厌这种大胆的说法。我没有实现私有实例构造函数的事实并没有破坏避免我在回答中描述的情况的要点。
- 我的观点不是要初始化。单例实现应该几乎创建单个实例。在你的代码中你在哪里做的?任何人都可以创建任意数量的A实例。像这样,new A()。
- @Mat&237;Asfidmraizer:我不在乎你是否讨厌这个说法,因为恨是主观的。我非常关心你是否同意或者逻辑上反驳它。
- @Mat&237;Asfidmraizer:您提供的代码并不是单一的…就这么简单。除非您阻止其他代码实例化它,否则您没有单例代码。
- @我已经修好了。我同意你的观点,代码约束比编码约定更好。如果我考虑一个类A singleton,我不会在某个地方自己实例化它,但是由于您已经可以检查我更新的答案,我已经将整个私有构造函数添加到A中,以防止出现这种用例。
- @mat&237;asfidmraizer:既然您添加了一个私有的A构造函数,那么B的定义就不会编译。那么这个问题的答案在哪里呢?
- @mat&237;asfidmraizer:这也阻止了B编译,除非它是嵌套类型…考虑到这是公开的,你仍然可以拥有A a1 = new B(); A a2 = new B();——它是怎样的一个单件?
- 为什么?我不明白为什么不是单身。看另一个答案,它有什么特别的不同?另一个答案实例化Instance属性getter中的单个实例。
- @mat&237;asfidmraizer:单实例不仅仅提供一个实例-它确保它是唯一创建的实例。参见en.wikipedia.org/wiki/singleton_pattern"在软件工程中,singleton模式是一种设计模式,它将类的实例化限制为一个对象。"您的代码不会这样做。
- 现在,您的类B甚至不会编译——这会使您的所有答案失效。
- @我已经撤走了私人建筑工人。我不应该在不考虑我之前要做什么的情况下编辑我的答案。
- 我的爆米花呢…
- @Mikkoviitala是和Micheal Jackson一起的Lol:P
- @Mat&237;诚恳地说,你现在正处于"鸡和蛋的问题"。你的回答再也不谈单身汉了。
- @琼斯基特很尊敬你,因为我认为你在这方面很聪明。但是,那么,当实现只是一个公共类时,是否考虑将控制生命周期管理的反转设置为singleton,而不是singleton?
- @Mat&237;Asfidmraizer:在正常情况下,它不是一个单体,不是。它不是单体模式。它试图达到类似的效果,而不是真正的独生子。注意,在单例模式中,"单例性"是类本身固有的,而不是类的使用。
- @如果我们需要讨论单例模式,我们也可以讨论它可以在没有访问修饰符的语言中实现。您是否仍然认为不能约束singleton类以避免其公共实例化会使整个设计模式失效?我们需要更灵活…顺便说一句,这只是我的意见。
- 我已经用更多的细节编辑了我的答案,并回答了你的问题…我相信我没那么错,但遗憾的是,今天有太多的奥托多西。
- @Mat&237;Asfidmraizer:是的,在这一点上,我认为你不能说你正在实现单例模式。您可以决定以单实例的方式使用某些内容,但这不是单例模式,也不是单例类。请注意,msdn文章是如何引用的、wikipedia文章以及除您之外的所有人使用的基本上每一个术语都是关于同一个概念的:一个类,它防止创建多个实例。既然命名设计模式的目的之一是为了避免混淆,为什么要将这两个概念混为一谈呢?
- @如果你看到我编辑过的答案,在这里我建议一个更方便的实现,它必须是单例的。
- @Jonskeet Imho,你在我的答案中集中了你的精力在示例单例实现上,不仅是你,还有其他评论批评这一部分的人已经忘记了,如果你不密封单例类,我在答案中描述的问题就存在。
- @Mat&237;Asfidmraizer:我恐怕现在很难理解你的答案,因为它到处都是,而且很多答案都是关于简单的非单件课程。以您的第一个类为例,介绍了"例如,请参阅下面的C中的简单单例实现",但我可以称之为A a1 = new A(); A2 = new A();,因此它不是问题中涉及的所有人(以及维基百科和msdn定义)所理解的单例类。
- @不幸的是,JonskeetBTW,Answer的评论并不是广泛讨论某些设计模式实现的最佳场所。我将尝试在我的不知名和不起眼的博客中发布为什么我认为我的示例实现是单例模式,由模式本身描述的需求所构成的需求…
- @Jonskeet您提到的第一个实现是错误的。谁在讨论这个?我试图在操作的皮肤中,使用继承给操作一个错误的单例实现示例。这就是全部。
- 如果您知道第一个示例不是单例实现,那么应该从答案中删除它。怎么会有人想知道你的答案中有哪些是你仍然相信的,哪些是你不相信的?的问题比B电流早得多。你根本不需要从中获得灵感来打破单身的感觉…
- @因为我在整个回答中说这是错误的。我会编辑让它更清晰
- 但您还没有解决这样一个事实:您已经做了两件事:使它成为一个密封类并添加了一个私有构造函数。将构造函数设为私有是必需的-严格来说,将类设为密封不是必需的。我觉得值得做,但没必要。此外,你对单件事物的离题是什么感觉它与所问的问题无关,这显然是指单件类/单件模式的经典定义,而不是DI风格的单件用法,这是非常不同的。
- @我还在编辑答案。
- 好吧,恐怕我已经做完了。这将是我对这个答案的最后评论。
- @这取决于你,我不是在为你编辑答案,只是我想在这个话题上发表我的观点。我已经编辑了很多细节,我真的相信这些细节可能不会给OP增加任何价值,但它可以帮助他,也可以让投票人理解我的观点。