当类具有静态构造函数时,将初始化静态字段 [英] Static field is initialized later when the class has a static constructor

查看:122
本文介绍了当类具有静态构造函数时,将初始化静态字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

通过运行以下简单代码:

By running this simple code:

class Program
{
    class MyClassWithStatic
    {
        public static int Number = SomeService.GetData();

        static MyClassWithStatic()
        {
            Console.WriteLine("static ctor runs");
        }
    }

    class SomeService
    {
        public static int GetData()
        {
            Console.WriteLine("GetDataRuns");
            return 42;
        }
    }        

    static void Main(string[] args)
    {
        InitService();

        var value = MyClassWithStatic.Number;
        Console.WriteLine(value);
    }

    private static void InitService()
    {
        Console.WriteLine("InitServiceRuns");
    }
}

我的机器上的输出是这样的:

The output on my machine is this:

InitServiceRuns
GetDataRuns
静态ctor运行
42

InitServiceRuns
GetDataRuns
static ctor runs
42

意味着首先调用InitService方法,然后初始化MyClassWithStatic的静态字段,然后调用静态构造函数(实际上,通过在ILSpy和IlDasm中查看此内容,我们可以看到静态字段的初始化发生在cctor的开头)

Meaning first the InitService method is called, then the static field of the MyClassWithStatic is initialized and then the static constructor is called (in fact by looking at this in ILSpy and IlDasm we can see that the initialization of the static fields happens at the beginning of the cctor)

在这一点上没有什么有趣的,一切都说得通,但是当我删除MyClassWithStatic的静态构造函数时(所以MyClassWithStatic变成了这个,其他所有内容都像以前一样)

There is nothing interesting at this point, everything makes sense, but when I remove the static constructor of the MyClassWithStatic (so MyClassWithStatic becomes this, and everything else remains as before)

class MyClassWithStatic
{
    public static int Number = SomeService.GetData();
}

输出是这样的:

GetDataRuns
InitServiceRuns
42

GetDataRuns
InitServiceRuns
42

这意味着通过删除静态构造函数,可以更早地初始化静态字段.由于初始化是静态构造函数的一部分(我使用ildasm进行了介绍),因此效果基本上是更早地调用了静态构造函数.

This means by removing the static constructor the static fields are initialized earlier. Since the initialization is a part of the static constructor (I tell this by looking into it with ildasm) the effect is basically that the static constructor is called earlier.

所以这是一个问题:

  1. 有人可以解释这种行为吗?可能是什么原因?

  1. Can someone explain this behaviour? What can be the reason for this?

调用静态构造函数时,是否还有其他可以更改的内容? (例如,附加探查器或在IIS中运行探查器等)(我比较了调试,发布模式,x86,x64以及所有这些都显示出相同的行为)

Is there any other thing which can change when the static constructor is called? (E.g. attaching a profiler or running it in IIS, etc.) (I compared debug, release mode, x86,x64 and all show the same behavior)

一些一般的事情:

-这是在.NET 4.6控制台应用程序中.我还切换到了.NET 2(应该使用其他clr运行,并且行为相同,没有任何区别)

-This was in a .NET 4.6 console application. I also switched to .NET 2 (should run with a different clr, and the behaviour is the same, it doesn't make any difference)

-我也在.NET核心上进行了尝试:无论是否使用cctor,都首先调用InitService方法.

-I also tried this with .NET core: both with and without a cctor the InitService method is called first.

-现在,我完全了解此页面:

-Now I'm absolutely aware of this page:

用户无法控制何时在以下位置执行静态构造函数 该程序.

The user has no control on when the static constructor is executed in the program.

我也知道,在静态构造函数中,您不应该做很多事情.但是不幸的是,我不得不处理一个代码,该部分不在我的控制范围之内,而我所描述的差异却产生了巨大的差异. (而且我也经历了许多与C#cctor相关的SO问题..)

And I also know that in a static constructor there are many things you shouldn't do. But unfortunately I have to deal with a code where this part is outside of my control and the difference I described makes a huge difference. (And I also went through many C# cctor related SO questions..)

(还有问题nr3 :)所以我描述的整个内容不是有点问题吗?

(And Question nr3:) So isn't the whole thing I described a little bit problematic?

推荐答案

有人可以解释这种行为吗?可能是什么原因?

Can someone explain this behaviour? What can be the reason for this?

@JonSkeet在 C#中的有关深度字段的段落中有一段关于静态字段和静态构造函数.这是一个片段:

@JonSkeet has a paragraph in C# in Depth about static fields and static constructors. Here's a snippet:

C#规范指出:

The C# specification states:

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

  • The static constructor for a class executes at most once in a given application domain. The execution of a static constructor is triggered by the first of the following events to occur within an application domain:

  • 已创建该类的实例.
  • 类被引用.
  • An instance of the class is created.
  • Any of the static members of the class are referenced.

CLI规范(ECMA 335)在 第8.9.5节:

The CLI specification (ECMA 335) states in section 8.9.5:

类型可以具有类型初始化器方法,也可以不具有.类型可能是 被指定为具有类型初始化器方法的松弛语义 (为方便起见,我们将这种宽松的语义称为BeforeFieldInit):.

A type may have a type-initializer method, or not. A type may be specified as having a relaxed semantic for its type-initializer method (for convenience below, we call this relaxed semantic BeforeFieldInit):.

  • 如果标记为BeforeFieldInit,则该类型的初始化方法为 在第一次访问任何静态字段时或之前的某个时间执行 为该类型定义.
  • 如果未标记BeforeFieldInit,则该类型为 初始化方法的执行((at(即被触发):首先 访问该类型的任何静态或实例字段,或者首先 调用该类型的任何静态,实例或虚拟方法))
  • If marked BeforeFieldInit then the type's initializer method is executed at, or sometime before, first access to any static field defined for that type.
  • If not marked BeforeFieldInit then that type's initializer method is executed ((at (i.e., is triggered by): first access to any static or instance field of that type, or first invocation of any static, instance or virtual method of that type))

这将向您显示,当类型不具有beforefieldinit标志时,运行时可以在任意时间内调用它,因为它在第一次访问定义的任何静态字段之前,即正是您所看到的.

This goes to show you that when a type doesn't have the beforefieldinit flag, the run-time may invoke it in an arbitrary time, given that it is before the first access to any static field defined, which is exactly what you're seeing.

在静态构造函数中还有其他可以更改的东西吗? 被称为?

Is there any other thing which can change when the static constructor is called?

唯一的事情是在您的类型上创建一个静态类型构造函数.否则,您将无法控制它的调用.

The only thing is creating a static type constructor on your type. Otherwises, you have no control over it's invocation.

所以我描述的整个内容不是有点问题吗?

So isn't the whole thing I described a little bit problematic?

在什么方面有问题?只要您知道自己要干什么,我都认为没有问题. CLI规范非常明确地说明了使用类型初始值设定项和不使用类型初始值设定项的保证.因此,如果您遵循这些准则,就不会有歧义.

Problematic in what regards? I see no problem as long as you know what you're in for. The CLI specification makes it absolutely clear as to what guarantees you have with a type initializer and without. Thus, if you follow those guidelines there should be no ambiguity.

这篇关于当类具有静态构造函数时,将初始化静态字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆