为什么 CLR 不总是调用值类型构造函数 [英] Why doesn't the CLR always call value type constructors

查看:27
本文介绍了为什么 CLR 不总是调用值类型构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个关于值类型中的类型构造函数的问题.这个问题的灵感来自 Jeffrey Richter 通过 C# 3rd ed 在 CLR 中写的东西,他说(在第 195 页 - 第 8 章)你永远不应该在值类型中实际定义类型构造函数,因为有时 CLR 不会调用

因此,例如(嗯……实际上是杰弗里·里希特斯的例子),即使查看 IL,我也无法弄清楚为什么在以下代码中没有调用类型构造函数:

内部结构 SomeValType{静态 SomeValType(){Console.WriteLine("这永远不会显示");}公共 Int32 _x;}公开密封类程序{静态无效主(字符串 [] args){SomeValType[] a = new SomeValType[10];[0]._x = 123;Console.WriteLine(a[0]._x);//显示123}}

因此,对类型构造函数应用以下规则,我无法理解为什么根本不调用上面的值类型构造函数.

  1. 我可以定义一个静态值类型构造函数来设置类型的初始状态.
  2. 一个类型最多只能有一个构造函数 - 没有默认构造函数.
  3. 类型构造函数是隐式私有的
  4. JIT 编译器检查该类型的类型构造函数是否已在此 AppDomain 中执行.如果不是,它会向本机代码发出调用,否则它不会因为它知道类型已经初始化".

所以...我就是不明白为什么我看不到正在构造的这种类型的数组.

我最好的猜测是:

  1. CLR 构造类型数组的方式.我本以为在创建第一个项目时会调用静态构造函数
  2. 构造函数中的代码没有初始化任何静态字段,因此被忽略.我已经尝试在构造函数中初始化私有静态字段,但该字段仍然是默认的 0 值 - 因此不会调用构造函数.
  3. 或者……由于设置了公共 Int32,编译器正在以某种方式优化构造函数调用 - 但这充其量只是一个模糊的猜测!

除了最佳实践等,我对它非常感兴趣,因为我希望能够亲眼看看为什么它没有被调用.

我在下面添加了我自己的问题的答案,只是引用杰弗里·里希特 (Jeffrey Richter) 对此的看法.

如果有人有任何想法,那就太好了.非常感谢,詹姆斯

解决方案

Microsoft C#4 规范与以前的版本相比略有变化,现在更准确地反映了我们在此处看到的行为:

<块引用>

11.3.10 静态构造函数

结构体的静态构造函数如下大多数规则与类相同.静态构造函数的执行对于结构类型由以下事件中的第一个发生在应用程序域内:

  • 引用了结构类型的静态成员.
  • 调用显式声明的 struct 类型的构造函数.

默认值的创建(§11.3.4) 的结构类型不触发静态构造函数.(一个这个例子是初始值数组中的元素.)

ECMA 规范Microsoft C#3 规范 在该列表中都有一个额外的事件:引用了结构类型的实例成员".所以看起来 C#3 在这里违反了它自己的规范.C#4 规范与 C#3 和 4 的实际行为更加一致.

编辑...

经过进一步调查,似乎几乎所有实例成员访问除了直接字段访问都会触发静态构造函数(至少在 C#3 和 4 的当前 Microsoft 实现中).>

因此,与 C#4 规范中的规则相比,当前的实现与 ECMA 和 C#3 规范中给出的规则的相关性更紧密:访问所有实例成员时,C#3 规则的实现是正确的except 字段;C#4 规则针对字段访问正确实施.

(当涉及到与静态成员访问和显式声明的构造函数相关的规则时,不同的规范都是一致的——并且显然是正确实现的.)

I have a question concerning type constructors within a Value type. This question was inspired by something that Jeffrey Richter wrote in CLR via C# 3rd ed, he says (on page 195 - chapter 8) that you should never actually define a type constructor within a value type as there are times when the CLR will not call it.

So, for example (well...Jeffrey Richters example actually), I can't work out, even by looking at the IL, why the type constructor is not being called in the following code:

internal struct SomeValType
{
    static SomeValType()
    {
        Console.WriteLine("This never gets displayed");
    }
    public Int32 _x;
}
public sealed class Program
{
    static void Main(string[] args)
    {
        SomeValType[] a = new SomeValType[10];
        a[0]._x = 123;
        Console.WriteLine(a[0]._x);     //Displays 123
    }
}

So, applying the following rules for type constructors I just can't see why the value type constructor above is not called at all.

  1. I can define a static value type constructor to set the initial state of the type.
  2. A type can have no more than one constructor - there is no default one.
  3. Type constructors are implicitly private
  4. The JIT compiler checks whether the type's type constructor has already been executed in this AppDomain. If not it emits the call into native code, else it doesn't as it knows the type is already 'initialized'.

So...I just can't work out why I can't see this type array being constructed.

My best guess would be that it could be:

  1. The way that the CLR constructs a type array. I would have thought that the static constructor would be called when the first item was created
  2. The code in the constructor is not initializing any static fields so it is ignored. I have experimented with initializing private static fields within the constructor but the field remains the default 0 value - therefore the constructor is not called.
  3. Or...the compiler is somehow optimizing away the constructor call due to the public Int32 being set - but that is a fuzzy guess at best!!

Best practices etc asside, I am just super intrigued by it as I want to be able to see for myself why it doesn't get called.

EDIT: I added an answer to my own question below, just a quote of what Jeffrey Richter says about it.

If anyone has any ideas then that would be brilliant. Many thanks, James

解决方案

The Microsoft C#4 Spec has changed slightly from previous versions and now more accurately reflects the behaviour that we're seeing here:

11.3.10 Static constructors

Static constructors for structs follow most of the same rules as for classes. The execution of a static constructor for a struct type is triggered by the first of the following events to occur within an application domain:

  • A static member of the struct type is referenced.
  • An explicitly declared constructor of the struct type is called.

The creation of default values (§11.3.4) of struct types does not trigger the static constructor. (An example of this is the initial value of elements in an array.)

The ECMA Spec and the Microsoft C#3 Spec both have an extra event in that list: "An instance member of the struct type is referenced". So it looks as if C#3 was in contravention of its own spec here. The C#4 Spec has been brought into closer alignment with the actual behaviour of C#3 and 4.

EDIT...

After further investigation, it appears that pretty much all instance member access except direct field access will trigger the static constructor (at least in the current Microsoft implementations of C#3 and 4).

So the current implementations are more closely correlated with the rules given in the ECMA and C#3 specs than those in the C#4 spec: the C#3 rules are implemented correctly when accessing all instance members except fields; the C#4 rules are only implemented correctly for field access.

(The different specs are all in agreement -- and apparently correctly implemented -- when it comes to the rules relating to static member access and explicitly declared constructors.)

这篇关于为什么 CLR 不总是调用值类型构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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