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

简介: 1.BeforeFieldInit是什么   前段时间在反编译代码时无意间看到在类中有一个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特性时当有一个静态变量被使用时就初始化所有静态变量

 

相关文章
|
25天前
|
Java 物联网 C#
C#/.NET/.NET Core学习路线集合,学习不迷路!
C#/.NET/.NET Core学习路线集合,学习不迷路!
|
18天前
|
开发框架 .NET C#
在 ASP.NET Core 中创建 gRPC 客户端和服务器
本文介绍了如何使用 gRPC 框架搭建一个简单的“Hello World”示例。首先创建了一个名为 GrpcDemo 的解决方案,其中包含一个 gRPC 服务端项目 GrpcServer 和一个客户端项目 GrpcClient。服务端通过定义 `greeter.proto` 文件中的服务和消息类型,实现了一个简单的问候服务 `GreeterService`。客户端则通过 gRPC 客户端库连接到服务端并调用其 `SayHello` 方法,展示了 gRPC 在 C# 中的基本使用方法。
30 5
在 ASP.NET Core 中创建 gRPC 客户端和服务器
|
8天前
|
开发框架 缓存 .NET
GraphQL 与 ASP.NET Core 集成:从入门到精通
本文详细介绍了如何在ASP.NET Core中集成GraphQL,包括安装必要的NuGet包、创建GraphQL Schema、配置GraphQL服务等步骤。同时,文章还探讨了常见问题及其解决方法,如处理复杂查询、错误处理、性能优化和实现认证授权等,旨在帮助开发者构建灵活且高效的API。
18 3
|
28天前
|
Java 物联网 编译器
C#一分钟浅谈:.NET Core 与 .NET 5 区别
本文对比了 .NET Core 和 .NET 5,从历史背景、主要区别、常见问题及易错点等方面进行了详细分析。.NET Core 侧重跨平台支持和高性能,而 .NET 5 在此基础上统一了 .NET 生态系统,增加了更多新特性和优化。开发者可根据具体需求选择合适的版本。
45 7
|
25天前
|
人工智能 开发框架 前端开发
C#/.NET/.NET Core技术前沿周刊 | 第 12 期(2024年11.01-11.10)
C#/.NET/.NET Core技术前沿周刊 | 第 12 期(2024年11.01-11.10)
|
25天前
|
开发框架 网络协议 .NET
C#/.NET/.NET Core优秀项目和框架2024年10月简报
C#/.NET/.NET Core优秀项目和框架2024年10月简报
|
2月前
|
开发框架 缓存 监控
NET Framework 到 .NET 5/6 的迁移是重大的升级
本文详细介绍了从 .NET Framework 4.8 迁移到 .NET 5/6 的过程,通过具体案例分析了迁移策略与最佳实践,包括技术栈评估、代码迁移、依赖项更新及数据库访问层的调整,强调了分阶段迁移、保持代码可维护性及性能监控的重要性。
58 3
|
24天前
|
人工智能 开发框架 安全
C#/.NET/.NET Core技术前沿周刊 | 第 13 期(2024年11.11-11.17)
C#/.NET/.NET Core技术前沿周刊 | 第 13 期(2024年11.11-11.17)
|
2月前
|
人工智能 开发框架 Cloud Native
C#/.NET/.NET Core技术前沿周刊 | 第 9 期(2024年10.07-10.13)
C#/.NET/.NET Core技术前沿周刊 | 第 9 期(2024年10.07-10.13)
NET Framework安装失败的麻烦
本人机子环境是安装了VS2012,即安装了 .NET Framework4.5,现在要安装AutoCAD2013,而安装CAD2013需要安装4.0的Framework,由于本机已有高版本的Framework,安装自然报错: 又不想来卸载4.5,觉得麻烦,但又想装上CAD2013,唯一的方法是修改Setup.ini初始化文件。
1065 0