关于.net:单个类密封是否真的有助于实现单例模式C#的目标 – 静态初始化(线程安全解决方案)

Does making singleton class sealed really help in achieving goal of singleton pattern C# - Static Initialization (A thread safe solution)

我知道关于这个问题还有其他线索,但我找不到令人满意的答案。为什么单例类需要用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命名约定)。


不一定是

在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;
      }
   }
}

将其密封将使其不可能继承(消除了单个实例的几个可能原因)


原因与实现细节密切相关。好的。

例如,在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。好的。好啊。