【问题标题】:Does reflection methods such as GetField and GetValue perform "referencing" or "access" in ECMA specification terms?GetField 和 GetValue 等反射方法是否执行 ECMA 规范术语中的“引用”或“访问”?
【发布时间】:2017-06-01 16:28:28
【问题描述】:

我真正想知道的是哪些反射方法会触发类型初始化?我有点不清楚。具体来说,如果将上述两个方法 GetField 和 GetValue 应用于静态字段,是否会触发类型初始化?我已经尝试过调查此事,据我了解,执行引用或访问静态字段等操作触发所有静态字段的类型初始化。下面我引用了我认为相关的规范部分,但使用“引用”和“访问”等措辞正是我犹豫的原因:
哪些操作实际上可以称为“访问”?
获取字段的 FieldInfo 元数据算作“引用”还是“访问”该字段?

请帮助我找到规范的相关部分,以便我知道我的代码* 是安全且规范的。合规,并且不会因为一些未记录的实现细节或行星恰好对齐而简单地“发生工作”。

*我的代码通过了测试,但依赖于类型初始化行为。我的代码没有在这里显示,因为它很冗长,问题不在于我只是想要一个“你的代码看起来不错”的回复,而是我想了解如何以及为什么这样我可以评估我的代码是否符合规范。我自己是否合规,以及(假设它合规)我可以和不能对其进行哪些更改的原因,而不必每次都提出新问题。


到目前为止,我知道以下规范,其中使用了上述术语“引用”和“访问”:

我知道ECMA-334 (C# 语言规范),静态字段初始化,第 17.4.5.1 节

如果是静态构造函数(§17.11) 存在于类中,静态字段初始值设定项的执行发生在执行该静态之前 构造函数。否则,静态字段初始化器在依赖于实现的时间执行 第一次使用该类的静态字段。

也知道ECMA-334 (C# 语言规范),静态构造函数,第 17.11 节

非泛型类的静态构造函数在给定的应用程序域中最多执行一次。静态的 对于每个封闭的构造类型,泛型类声明的构造函数最多执行一次 从类声明(§25.1.5)构造。静态构造函数的执行由第一个触发 在应用程序域中发生以下事件:

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

如果一个类包含开始执行的 Main 方法(第 10.1 节),则该类的静态构造函数 在调用 Main 方法之前执行。如果一个类包含任何带有初始化器的静态字段,那些 初始化程序在执行静态构造函数之前按文本顺序执行(第 17.4.5 节)。

更相关的是ECMA-335 (CLI 规范),类类型定义,第一部分,第 8.9.5 节

[...] 触发此类初始化方法执行的时间和内容的语义如下:

  1. 类型可以有类型初始化方法,也可以没有。
  2. 可以将类型指定为其类型初始化器方法具有宽松语义(为了下面的方便,我们将这种宽松语义称为 BeforeFieldInit)。
  3. 如果标记为 BeforeFieldInit,则该类型的初始化方法会在首次访问为该类型定义的任何静态字段时或之前的某个时间执行。
  4. 如果未标记 BeforeFieldInit,则该类型的初始化方法在以下位置执行(即由以下位置触发):
    一个。首次访问该类型的任何静态字段,或
    乙。首次调用该类型的任何静态方法,或
    c。如果它是值类型,则首次调用该类型的任何实例或虚拟方法,或者
    d.首次调用该类型的任何构造函数。
  5. 执行任何类型的初始化方法都不会触发由其基类型定义的任何初始化方法的自动执行,也不会触发该类型实现的任何接口。

相关 MSDN 链接:
Type.GetField Method
FieldInfo Class
FieldInfo.GetValue Method

【问题讨论】:

  • 这如何保证对“基于意见”的密切投票?规格有时会令人困惑,我只想知道我是否正确解释了它们。如果幸运的话,其中一位设计师会回答它,因此不会有任何基于意见的内容。
  • 是的,这是一个奇怪的接近投票。无论如何,反射不在 C# 规范的范围之内,因为它是 CLI 的东西,而不是 C# 的东西。反射方法调用基本上是指示运行时发出和/或执行相关的 IL,这反过来又调用适当主题的 C# 规范中的相关规则,例如类型初始化,其中 C# 和 CLI 之间的重叠很重(基本上是C# 规范承诺 CLI 的东西,而不是 C# 特定的东西)。但这只是我头脑中的一个解释,我真的找不到更准确的解释来源。
  • 好吧,我想说仅仅检查类型信息(例如GetField())并不能算作“访问”或“引用”实例或类型,但是只要你打电话给FieldInfo.GetValue(),它就可以了。但这是一种未受过教育的直觉,我无法确认,因此没有作为答案发布。
  • 单独检查 Type 对象不会触发此构造函数,也不会检查字段、属性、方法或类型本身的属性。
  • @hazzik 我刚刚重读了您的题外话链接。对我来说似乎很有话题。也许自从你上次自己阅读后它已经被修改过? “如果您的问题通常涵盖软件开发特有的实际、可回答的问题,那么您来对地方提出问题了!”据我所知,该声明之后的任何警告均不适用。 (但感谢您抽出宝贵时间发表评论,这很有帮助,如果我错了,我想了解原因。)

标签: c# reflection specifications typeinitializer


【解决方案1】:

我真正想知道的是哪些反射方法会触发类型初始化?

[...]

具体来说,GetField 和 GetValue 这两个方法如果应用于静态字段,是否会触发类型初始化?

FieldInfo.GetValue 触发类型初始化。来自实验观察。这一切都取决于实现,并且不会有任何证明。它不需要在所有情况下都有效,因为反射不需要遵循任何规范,因为规范不包括反射。有一些 signs 可以得到一个未初始化的字段,但我无法生成代码来实现它。

typeof()Type.GetTypeType.GetField 很可能不会触发类型初始化。但同样,这是来自观察。

如果您需要确保您的类型初始化程序将在任何特定时间/之前被调用,您需要调用RuntimeHelpers.RunClassConstructor 方法。这是唯一保证类型初始化器将被调用并且在应用域的生命周期中只被调用一次的方法。

哪些操作实际上可以称为“访问”?

这些操作都不是,因为反射也不在规范中,因此这些术语在这里不适用。

获取字段的 FieldInfo 元数据算作“引用”还是“访问”该字段?

都没有。

从规范中不清楚,但这就是我理解“访问”和“引用”之间的区别的方式:

  • 访问是当您调用此成员(方法或属性)或获取/设置其值(字段)时
  • 当方法的主体具有对该成员的访问表达式时,该方法引用该成员。

PS:目前还不清楚你在问什么以及你试图解决什么具体问题。

【讨论】:

  • 是的,我最终使用了 RuntimeHelpers.RunClassConstructor。我想解决的问题是确保当我在静态字段上调用 ​​GetValue 时不会得到未初始化的值。我确实最终重新编写了我的代码,以便在大多数情况下我可以保证在我反映它们之前初始化这些字段,但仍有一种情况我不能,这就是我最终使用RunClassConstructor 的地方。就是这样:我想知道该值不是未初始化的 - 但询问它是否触发初始化更聪明,因为即使是正常访问也可以返回未初始化的值。
  • 当 CLI 规范详细说明 Compact Profile(甚至不需要浮点支持)要求存在“反射库”。你怎么能要求某物的存在而不指定某物应该如何工作..?幽默地说,这就像在说“你必须有一个 Finkelding 在那里——我不知道它是什么,但它必须在那里!如果你没有 Finkelding,你就不会合规!”这肯定不会让我开心。 :(
  • @AnorZaken 我认为你需要把这个第一条评论和没有工作(和工作)的案例放在问题上,以便更清楚
  • 我真的只想知道是否触发了类型初始化。没有什么我需要帮助的。它很复杂,但基本上一个泛型类在我的库之外的一个类中定义了一堆静态字段,并且从一个将该类型作为泛型类型参数的方法中,我需要确保该类型的所有静态字段都已初始化。因为我已经知道访问一个静态字段将触发同一类上所有静态字段的初始化,所以我需要的只是反射保证或触发它的另一种方法,即 RunClassConstructor。
  • 并且需要明确的是,这并不是我有一个不起作用的案例 - 而是我有一个没有保证工作的案例。谁想发布不能保证有效的代码。坐得不好。
猜你喜欢
  • 2021-09-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-02-24
相关资源
最近更新 更多