Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/24.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 单例-如果或尝试/捕获?_C#_.net_Singleton - Fatal编程技术网

C# 单例-如果或尝试/捕获?

C# 单例-如果或尝试/捕获?,c#,.net,singleton,C#,.net,Singleton,我已经考虑这个想法很长时间了,我想听听你们的想法 编写单身汉的标准习惯用法大致如下: public class A { ... private static A _instance; public static A Instance() { if(_instance == null) { _instance = new A(); } return _instance; } ... } 在这里,我提出另一个解决方案:

我已经考虑这个想法很长时间了,我想听听你们的想法

编写单身汉的标准习惯用法大致如下:

public class A {
...
   private static A _instance;

   public static A Instance() {
      if(_instance == null) {
          _instance = new A();
      }

      return _instance;
   }
...
}
在这里,我提出另一个解决方案:

public class A {
...
   private static A _instance;

   public static A Instance() {
       try {
         return _instance.Self();
       } catch(NullReferenceExceptio) {
         _instance = new A();
       }           

       return _instance.Self();
   }

   public A Self() {
       return this;
   }
...
}
它背后的基本思想是,1次取消引用和取消引用异常的运行时成本小于一次空检查的运行时成本。我已尝试测量潜在性能增益,以下是我的数字:

睡眠1秒(尝试/捕捉):188788ms

睡眠1秒(空检查):207485ms

以及测试代码:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;

public class A
{
   private static A _instance;
   public static A Instance() {
       try {
         return _instance.Self();
       } catch(NullReferenceException) {
         _instance = new A();
       }           

       return _instance.Self();
   }

   public A Self() {
       return this;
   }

    public void DoSomething()
    {
        Thread.Sleep(1);
    }
}

public class B
{
   private static B _instance;

   public static B Instance() {
      if(_instance == null) {
          _instance = new B();
      }

      return _instance;
   }

    public void DoSomething()
    {
        Thread.Sleep(1);
    }
}

public class MyClass
{
    public static void Main()
    {
        Stopwatch sw = new Stopwatch();
        sw.Reset();

        sw.Start();
        for(int i = 0; i < 100000; ++i) {
            A.Instance().DoSomething();
        }

        Console.WriteLine(sw.ElapsedMilliseconds);
        sw.Reset();

        sw.Start();
        for(int i = 0; i < 100000; ++i) {
            B.Instance().DoSomething();
        }
        Console.WriteLine(sw.ElapsedMilliseconds);

        RL();
    }

    #region Helper methods

    private static void WL(object text, params object[] args)
    {
        Console.WriteLine(text.ToString(), args);   
    }

    private static void RL()
    {
        Console.ReadLine(); 
    }

    private static void Break() 
    {
        System.Diagnostics.Debugger.Break();
    }

    #endregion
}
使用系统;
使用System.Collections.Generic;
使用系统线程;
使用系统诊断;
公共A类
{
私有静态A_实例;
公共静态实例(){
试一试{
返回_instance.Self();
}捕获(NullReferenceException){
_实例=新的A();
}           
返回_instance.Self();
}
公开的自我{
归还这个;
}
公共无效剂量测定法()
{
睡眠(1);
}
}
公共B级
{
私有静态B_实例;
公共静态B实例(){
if(_instance==null){
_实例=新的B();
}
返回_实例;
}
公共无效剂量测定法()
{
睡眠(1);
}
}
公共类MyClass
{
公共静态void Main()
{
秒表sw=新秒表();
sw.Reset();
sw.Start();
对于(int i=0;i<100000;++i){
A.实例().DoSomething();
}
控制台写入线(软件延迟毫秒);
sw.Reset();
sw.Start();
对于(int i=0;i<100000;++i){
B.实例().DoSomething();
}
控制台写入线(软件延迟毫秒);
RL();
}
#区域辅助方法
私有静态void WL(对象文本,参数对象[]args)
{
Console.WriteLine(text.ToString(),args);
}
私有静态void RL()
{
Console.ReadLine();
}
私有静态void Break()
{
System.Diagnostics.Debugger.Break();
}
#端区
}

由此带来的性能提升几乎是10%,问题是它是一个微操作,还是它可以为满足单例需求的应用程序(或者它的中间件,比如日志记录)提供显著的性能提升?

您要问的是实现糟糕单例模式的最佳方法。你应该看一下乔恩·斯基特的文章。你会发现有更好(更安全)的方法,它们不会出现同样的性能问题。

我认为如果我的应用程序经常调用单例构造函数,那么性能提升10%就意味着什么,我会非常担心

也就是说,两个版本都不是线程安全的。首先要集中精力把事情做好

“我们应该忘记小问题 效率,比如说大约97%的 时间:过早优化是关键 万恶之源。”


这种优化似乎微不足道。我一直被教导使用try/catch块只是为了捕捉那些很难或不可能用if语句检查的条件


并不是说你提出的方法行不通。我认为您的解决方案是完全可以接受的,尽管我认为在实现一个懒散加载的单点PANTE模式之前,应该考虑一些(很少)考虑的事情。
  • 你能提供一个DI版本吗?unity容器能满足接口契约吗?这将允许您在以后需要时交换实现,也使测试更加容易
  • 如果您必须坚持使用单例,那么您真的需要一个延迟加载的实现吗?在静态构造函数上隐式或显式创建实例的成本只有在类在运行时被引用时才会执行,而根据我的经验,几乎所有的单例都只有在它们想要访问“实例”时才会被引用
  • 如果您需要按照您描述的方式实现它,您可以按照以下方式来实现它

    更新:正如Brian Gideon指出的那样,我已将示例更新为锁类类型而不是锁变量,实例可能处于半初始化状态。强烈建议不要使用
    lock(typeof())
    ,我建议您永远不要使用这种方法

    public class A {
        private static A _instance;
        private A() { }
        public static A Instance {
            get {
                try {
                    return _instance.Self();
                } catch (NullReferenceException) {
                    lock (typeof(A)) {
                        if (_instance == null)
                            _instance = new A();
                    }
                }
                return _instance.Self();
            }
        }
        private A Self() { return this; }
    }
    

    “改进的”实现有一个大问题,那就是它触发了一个中断。经验法则是应用程序逻辑不应该依赖于异常触发

    永远不要对控制流使用异常处理

    此外,不是:

     if(_instance == null) {
              _instance = new A();
          }
    
          return _instance;
    
    你可以做:

    return _instance ?? (_instance = new A());
    
    这在语义上是相同的。

    这是一种可怕的方法。正如其他人所指出的,异常只应用于处理异常、意外情况。由于我们做出了这样的假设,许多组织在积极寻找和报告异常(甚至是已处理的异常)的上下文中运行其代码,因为已处理的空引用异常几乎肯定是一个bug。如果您的程序意外地取消了对无效内存的引用并愉快地继续运行,那么很有可能您的程序中出现了严重的、严重的错误,应该引起某人的注意

    不要“呼喊狼来了”,故意营造一种看起来很糟糕但实际上是有意为之的局面。这只是让每个人都有更多的工作。在C#中有一种标准的、直接的、被接受的方法来生成一个单例;如果你是这个意思,就这么做。不要试图发明一些违反良好编程原则的疯狂东西。比我聪明的人设计了一个有效的单例实现;不使用它是愚蠢的

    我通过艰苦的方式学会了这一点。长话短说,我曾经故意使用一个测试,在主线场景中,该测试在大多数情况下都会解除对坏内存的引用
    public class A {
    ...
    private static A _instance = new A();
    
    public static A Instance() {
      return _instance;
    }
    ...
    }