为什么不是';是否调用此C#实例构造函数,除非存在对非静态成员的引用?

为什么不是';是否调用此C#实例构造函数,除非存在对非静态成员的引用?,c#,static,.net-core,C#,Static,.net Core,此代码似乎没有调用Mixed构造函数并打印y=0 public class Mixed { public int x; public static int y; public Mixed() { x = 1; y = 1; } } public class Program { static Mixed mixed = new Mixed(); static void Main(string[]

此代码似乎没有调用
Mixed
构造函数并打印
y=0

public class Mixed
{
    public int x;
    public static int y;

    public Mixed()
    {
        x = 1;
        y = 1;
    }
}    

public class Program
{
    static Mixed mixed = new Mixed();

    static void Main(string[] args)
    {
        Console.WriteLine("y = " + Mixed.y);

        Console.ReadLine();
    }
}
但是,简单地修改
Main
函数使其看起来像这样会导致调用构造函数

static void Main(string[] args)
{   
    Console.WriteLine("x = " + mixed.x);
    Console.WriteLine("y = " + Mixed.y);

    Console.ReadLine();
}
这张照片是:

x = 1
y = 1
为什么简单地将此引用添加到非静态字段会导致正确调用构造函数?创建对象不应该总是导致调用构造函数,而不管该对象以后在程序中如何使用吗

奇怪的是,像这样使
Mixed
对象非静态也会导致调用构造函数:

public class Program
{
    static void Main(string[] args)
    {
        Mixed mixed = new Mixed();

        Console.WriteLine("y = " + Mixed.y);

        Console.ReadLine();
    }
}

然而,这对我来说似乎也没有意义。将
Mixed
对象声明为static应该只意味着内存中只有一个对象的副本,而不管程序实例化了多少次。这是某种编译器优化吗?对于静态字段,编译器在实际实例化它之前会等待对该类型的非静态字段的引用吗?

您遇到的是,
程序
类型的静态字段没有被初始化

您会发现,当第一次访问某一类型的任何静态字段时,它们都会被初始化。这在不同的运行时(.NET Framework、.NET Core、Mono)之间是一致的

如下面的IL代码所示,您的示例将生成一个初始化字段的
.cctor
(静态构造函数)

.class public auto ansi beforefieldinit ConsoleApp1.Program
    extends [System.Runtime]System.Object
{
    .field private static class ConsoleApp1.Mixed mixed

    // (...) omitted irrelevant methods

    .method private hidebysig specialname rtspecialname static 
        void .cctor () cil managed 
    {
        // Code size 11 (0xb)
        .maxstack 8

        IL_0000: newobj instance void ConsoleApp1.Mixed::.ctor()
        IL_0005: stsfld class ConsoleApp1.Mixed ConsoleApp1.Program::mixed
        IL_000a: ret
    }

}

但是,不同意在什么时候初始化字段。例如,NET Core将在访问任何字段之前延迟加载这些字段。NET Framework将在访问任何类型的成员(包括方法)时急切地加载字段

以下代码在.NETCore和.NETFramework中具有不同的结果

public class Program
{
    static Mixed mixed = new Mixed();

    static string text = "Hello, World!";

    static void Main(string[] args)
    {
        Console.WriteLine("y = " + Mixed.y);
        Console.WriteLine(text);
        Console.WriteLine("y = " + Mixed.y);

        Console.ReadLine();
    }
}
在.NET核心中:

y=0
你好,世界
y=1

在.NET Framework中:

y=1
你好,世界
y=1


如果向类中添加静态构造函数,则在访问任何字段或方法时,字段将始终初始化:

// ... adding to previous Program class
static Program()
{
    // empty body
}
在.NET核心中:

y=1
你好,世界
y=1

发生这种情况的原因是,
beforefieldinit
标志不会添加到具有自定义静态构造函数的类型中。此标志表示可以尽可能晚/晚加载类型的字段

当省略此标志时,字段将立即初始化。当标志出现时,字段至少会被延迟加载,但也可能被急切加载,这取决于运行时


如前所述,.NET Core将在标志出现时延迟加载字段。NET Framework将急切地加载字段,无论是否存在该标志。

启用优化后,
new Mixed()
调用将被删除,因为您没有引用
Mixed
。尝试在不进行优化(调试配置)的情况下运行代码,看看会发生什么。如果您有LINQPad,那么通过切换/o-标志来测试这是非常简单的。这是调试代码还是发布代码?“这段代码似乎没有调用
混合
构造函数并打印
y=0
”-无法确认。代码为我打印1。构造函数中的断点被命中。在这两种情况下我都得到1。@Sinatr我在.NET Core中运行它,您使用的是什么运行时?VS 2015:File-New-Project-Visual C#Console应用程序。我不确定您为什么被否决,我得到的是相同的输出。谢谢,这是有道理的。静态字段初始化是有保证的。不同的运行时(.NET Framework、.NET Core、调试版本和发布版本)可能会实现不同的功能,只要它们遵循保证。但是,这一切都是因为程序中存在未引用的静态数据会让你感到头疼。