.net 5.0 VS .net Framework 4.6 不同的行为

Mik*_*ike 5 c# .net-framework-version .net-5

注意这段代码,在.net Framework 4.6中运行时,只能得到“按任意键。”,但可以得到“配置已更改”。在.net 5.0中,为什么?

    public class Holder
    {
        public static int IntVal = 0;
        public static DateTime DateTimeVal;
    }

    public class StaticDateTime
    {
        private static DateTime config = DateTime.Now;

        public StaticDateTime()
        {

        }

        public void configChanged()
        {
            DateTime d = Holder.DateTimeVal;
            if (d.Ticks == 0)
            {
                Holder.DateTimeVal = DateTime.Now;
                return;
            }

            if (d < config)
            {
                Console.WriteLine("config changed.");
            }
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            StaticDateTime sd = new StaticDateTime();
            sd.configChanged();
            System.Threading.Thread.Sleep(1500);
            StaticDateTime sd2 = new StaticDateTime();
            sd2.configChanged();
            Console.WriteLine("press any key.");
            Console.ReadKey();
        }
    }
Run Code Online (Sandbox Code Playgroud)

在.net框架的逻辑中,所有静态成员都会在第一次引用时被初始化: https: //learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/静态类和静态类成员

程序无法准确指定类的加载时间。但是,在程序中首次引用该类之前,保证会加载该类并初始化其字段并调用其静态构造函数。

好像这个逻辑在.net core中不可用,有官方文档吗?

Jon*_*eet 5

引用的段落有点太宽松了。完全有可能在方法中执行代码而不初始化其包含的类。如果类没有静态构造函数,则类初始化必须在第一次引用静态字段之前以及创建第一个实例之前的某个时刻发生 - 但在此之前的任何时刻都可以发生。

\n

这是一种更简单的重现方法:

\n
using System;\n\nclass Program\n{\n    static int field = ReportInitialization();\n\n    private static int ReportInitialization()\n    {\n        Console.WriteLine("Field initializer called");\n        return 5;\n    }\n\n    static void Main()\n    {\n        Console.WriteLine("Before field reference");\n        int f = field;\n        Console.WriteLine($"Field value: {f}");\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

在我的机器上的 .NET 6.0 中,打印:

\n
Before field reference\nField initializer called\nField value: 5\n
Run Code Online (Sandbox Code Playgroud)\n

在我的机器上的 .NET 4.6.2 中,打印:

\n
Field initializer called\nBefore field reference\nField value: 5\n
Run Code Online (Sandbox Code Playgroud)\n

如果添加静态构造函数 ( static Program(){}),则两者具有与上面的 .NET 4.6.2 相同的输出。

\n

C# 标准有更精确的措辞。来自第 14.5.62 节

\n
\n

类的静态字段变量初始值设定项对应于一系列赋值,这些赋值按它们出现在类声明 (\xc2\xa714.5.6.1) 中的文本顺序执行。在分部类中,\xe2\x80\x9c 文本顺序\xe2\x80\x9d 的含义由\xc2\xa714.5.6.1 指定。如果类中存在静态构造函数 (\xc2\xa714.12),则在执行该静态构造函数之前立即执行静态字段初始值设定项。否则,静态字段初始值设定项将在首次使用该类的静态字段之前的依赖于实现的时间执行。

\n
\n

关于静态构造函数的第 14.12 节指出:

\n
\n

封闭类的静态构造函数在给定的应用程序域中最多执行一次。静态构造函数的执行由应用程序域内发生的以下第一个事件触发:

\n
    \n
  • 创建该类的一个实例。
  • \n
  • 类的任何静态成员都会被引用。
  • \n
\n
\n