• .NET Core中延迟单例另一种写法【.NET Core和.NET Framework的beforefieldinit差异】


    1.BeforeFieldInit是什么

       前段时间在反编译代码时无意间看到在类中有一个BeforeFieldInit特性,处于好奇的心态查了查这个特性,发现这是一个关于字段初始化时间的特性【提前初始化字段】,下面先来看一下这个特性在.net framework中的作用

    class Foo
    {
       public static String x = GetStr("初始化 Foo 静态成员字段");
       public static String GetStr(String str)
      {
           Console.WriteLine(str);
           return str;
      }
    }

       在上面Foo类中只定义了一个静态字段x和一个静态方法GetStr的方法,在这里需要关注的是静态字段x的初始化时机

    static void Main(string[] args)
    {
          Console.WriteLine("Main方法开始");
          Foo.GetStr("手动调用Foo.GetSring()方法");
          String y = Foo.x;
    }

      在Main中简单的调用静态方法和静态字段,我们知道静态字段的赋值是在静态构造函数中进行的,那么输出顺序应该是 “Main方法开始”,”初始化Foo静态成员字段“,”手动调用Foo.GetString()方法“,但是真的是这样吗,答案是错的

      可以看到静态成员字段的初始化是在最开始,那么为什么会这样呢,我们将代码反编译IL后会发现在类中具有一个beforefieldinit特性,

    .class private auto ansi beforefieldinit BeoreFieldInitTest2.Foo
        extends [mscorlib]System.Object
    {
    } // end of class BeoreFieldInitTest2.Foo

       那么BeforeFieldInit是什么,我找到了一篇文章有对BeforeFieldInit的详细讲解,在这里也不过多介绍,

    2.取消BeforeFieldInit加载

      那么该怎么取消beforefieldinit特性呢,其实很简单,只需要在类中加入一个静态构造函数即可

    class Foo
    {
         public static string x = GetStr("初始化 Foo 静态成员字段");
    //空的静态构造函数
    static Foo(){}
    public static String GetStr(String str) { Console.WriteLine(str); return str; } }

      然后此时输入就如我们所猜测那样

     并且反编译可以看到IL代码也取消了beforefieldinit特性

    .class private auto ansi BeoreFieldInitTest2.Foo
        extends [mscorlib]System.Object
    {
    } // end of class BeoreFieldInitTest2.Foo

      下面就该进入正题,来看看.NET Core中不一样的BeforeFieldInit  

    3.BeforeFieldInit在.NET Core 中的差异

      将最开始的代码在.NET Core中跑一跑会发现跟.NET Framework不一样的操作

    class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Main方法开始");
                Foo.GetStr("手动调用Foo.GetSring()方法");
                String y = Foo.x;
            }
        }
        class Foo
        {
            public static string x = GetStr("初始化 Foo 静态成员字段");
            public static String GetStr(String str)
            {
                Console.WriteLine(str);
                return str;
            }
        }

      可以看到在.NET Core并没有像.NET Framework那样进行提前加载,并且加载貌似还延迟了,反编译代码可以看到beforefieldinit特性还在Foo类上

    .class private auto ansi beforefieldinit BeforeFieldInitTest.Foo
        extends [System.Runtime]System.Object
    {
    } // end of class BeforeFieldInitTest.Foo

        那么在.NET Core加入静态构造函数会怎么呢?怀着各种疑惑进行测试

    class Program
    {
         static void Main(string[] args)
         {
              Console.WriteLine("Main方法开始");
              Foo.GetStr("手动调用Foo.GetSring()方法");
              String y = Foo.x;
          }
     }
    class Foo
    {
          public static string x = GetStr("初始化 Foo 静态成员字段");
          //空的静态构造函数
          static Foo() { }
          public static String GetStr(String str)
          {
              Console.WriteLine(str);
              return str;
          }
    }

        可以看到.NET Core中加入静态构造函数以后输出跟.NET Framework一致,也就说可以猜测.NET Core运行时对beforefieldinit特性进行了优化,当然这也只是我的猜测

    4.利用.NET Core中beforefieldinit实现的单例

       在.NET Framework中我们都是使用Lazy<>类来创建延迟加载单例,但是我们可以看到在.NET Core中beforefieldinit是延迟加载的,所以我们直接可以使用此方法来创建延迟安全单例,

    class Program
    {
        static void Main(string[] args)
        {
             Console.WriteLine("Main方法开始");
             Foo.GetStr("手动调用Foo.GetSring()方法");
             Console.WriteLine("我是分隔符");
              Console.WriteLine("我是分隔符");
              var foo= Foo.CreateInstance;
         }
    }
    class Foo
    {
         public static Foo CreateInstance { get;  } = new Foo();
         private Foo()
         {
             Console.WriteLine("创建了Foo实例");
         }
         public static String GetStr(String str)
         {
             Console.WriteLine(str);
             return str;
         }
     }

      运行结果可以看到创建实例被延迟了,

      当然,这种创建单例也是有缺点的,当类中还有其它静态字段或属性时,并且在外部进行了调用,那么此时也会初始化此属性

    class Program
    {
         static void Main(string[] args)
         {
             Console.WriteLine("Main方法开始");
             Foo.GetStr("手动调用Foo.GetSring()方法");
             var y = Foo.x;//调用静态字段/属性
             Console.WriteLine("我是分隔符");
             Console.WriteLine("我是分隔符");
             var foo= Foo.CreateInstance;
         }
     }
     class Foo
     {
         public static string x = GetStr("初始化 Foo 静态成员字段"); //加入了静态字段或属性
         //public static String X { get; set; } = GetStr("初始化 Foo 静态成员字段");
         public static Foo CreateInstance { get;  } = new Foo();
         private Foo()
         {
             Console.WriteLine("创建了Foo实例");
         }
         public static String GetStr(String str)
         {
             Console.WriteLine(str);
             return str;
         }
    }

       也就是说在.NET Core中beforfieldinit特性时当有一个静态变量被使用时就初始化所有静态变量

  • 相关阅读:
    Jython:java调用python文件之第三方包路径问题
    待研究的技术第二版
    mysql主从数据库不同步的2种解决方法(转)
    mysql分表和表分区详解
    mysql-binlog日志恢复数据库
    mysql delete删除记录数据库空间不减少问题解决方法
    mySQL 增量备份方案(转)
    mysql 查看 删除 日志操作总结(包括单独和主从mysql)
    Memcache的部署和使用(转)
    Linux下memcache的安装和启动(转)
  • 原文地址:https://www.cnblogs.com/yan7/p/9187874.html
Copyright © 2020-2023  润新知