zoukankan      html  css  js  c++  java
  • .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特性时当有一个静态变量被使用时就初始化所有静态变量

  • 相关阅读:
    LeetCode 515. 在每个树行中找最大值(Find Largest Value in Each Tree Row)
    LeetCode 114. 二叉树展开为链表(Flatten Binary Tree to Linked List)
    LeetCode 199. 二叉树的右视图(Binary Tree Right Side View)
    LeetCode 1022. 从根到叶的二进制数之和(Sum of Root To Leaf Binary Numbers)
    LeetCode 897. 递增顺序查找树(Increasing Order Search Tree)
    LeetCode 617. 合并二叉树(Merge Two Binary Trees)
    LeetCode 206. 反转链表(Reverse Linked List) 16
    LeetCode 104. 二叉树的最大深度(Maximum Depth of Binary Tree)
    LeetCode 110. 平衡二叉树(Balanced Binary Tree) 15
    LeetCode 108. 将有序数组转换为二叉搜索树(Convert Sorted Array to Binary Search Tree) 14
  • 原文地址:https://www.cnblogs.com/yan7/p/9187874.html
Copyright © 2011-2022 走看看