Does reflection methods such as GetField and GetValue perform "referencing" or "access" in ECMA specification terms?
Asked Answered
R

1

7

What I really want to know is which reflection methods will trigger type initialization? It's a bit unclear to me. Specifically, will the two mentioned methods, GetField and GetValue, trigger type initialization if applied to static fields? I've already tried looking into the matter, and as far as I understand performing actions such as referencing or accessing static fields will trigger type initialization of all static fields. Below I have quoted sections of the specification that I think are relevant, but the use of wording such as "referencing" and "access" is precisely where my hesitation stems:
What actions actually qualifies as "access"?
Does getting FieldInfo metadata for a field count as "referencing" or "accessing" the field?

Please help me find the relevant parts of the specifications so I know that my code* is safe and spec. compliant and doesn't simply "happen to work" because of some undocumented implementation detail or because the planets happen to be aligned just right.

*My code passes testing but relies on type initialization behavior. My code is not shown here because it is verbose and the question is not about me just wanting a "your code looks ok" reply, but rather I want to learn how and why such that I can asses whether my code is spec. compliant or not myself, and (assuming it is compliant) reason about what changes I can and cannot make to it without having to ask a new question every time I do so.


So far I know about the following pieces of the specifications, which uses the aforementioned terms "referencing" and "access":

I know about ECMA-334 (C# language specification), Static field initialization, section 17.4.5.1

If a static constructor (§17.11) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class.

Also know about ECMA-334 (C# language specification), Static constructors, section 17.11

The static constructor for a non-generic class executes at most once in a given application domain. The static constructor for a generic class declaration executes at most once for each closed constructed type constructed from the class declaration (§25.1.5). 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.

If a class contains the Main method (§10.1) in which execution begins, the static constructor for that class executes before the Main method is called. If a class contains any static fields with initializers, those initializers are executed in textual order immediately prior to executing the static constructor (§17.4.5).

And more relevantly ECMA-335 (CLI specification), Class type definition, part I, section 8.9.5

[...] The semantics of when and what triggers execution of such type initialization methods, is as follows:

  1. A type can have a type-initializer method, or not.
  2. A type can be specified as having a relaxed semantic for its type-initializer method (for convenience below, we call this relaxed semantic BeforeFieldInit).
  3. 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.
  4. If not marked BeforeFieldInit then that type’s initializer method is executed at (i.e., is triggered by):
    a. first access to any static field of that type, or
    b. first invocation of any static method of that type, or
    c. first invocation of any instance or virtual method of that type if it is a value type or
    d. first invocation of any constructor for that type.
  5. Execution of any type's initializer method will not trigger automatic execution of any initializer methods defined by its base type, nor of any interfaces that the type implements.

Related MSDN links:
Type.GetField Method
FieldInfo Class
FieldInfo.GetValue Method

Riti answered 1/6, 2017 at 16:28 Comment(14)
How does this warrant a close vote for "opinion based"? Specifications can be confusing sometimes, I just want to know if I'm interpreting them correctly. If I'm lucky one of the designers will answer it and there will thus be nothing opinion-based about it.Riti
Yeah, that's a weird close vote. Anyway reflection lies outside the domain of the C# specification, as it's a CLI thing, not a C# thing. Reflection method calls basically instruct the runtime to emit and/or execute the relevant IL, which in turn invoke the relevant rules from the C# specification of the appropriate subject, such as type initialization, where the overlap between C# and CLI is heavy (basically the C# specs promises CLI things there, not C#-specific things). But that's an off the top of my head explanation, I can't really find a definitive source that explains it more properly.Trifurcate
@Trifurcate Thank you: The part about "reflection following CLI spec" is very promising. Maybe even a full or partial answer. If it does, than it should count as "access" under bullet 3 and 4.a in my CLI spec. quote above, at least for GetValue, correct? That would then just leave us with GetField unanswered - does acquiring a FieldInfo instance of a field count as "accessing" that field (or satisfy any of the other rules).Riti
Well I'd say that merely inspecting type information (such as GetField()) doesn't count as "accessing" or "referencing" the instance or type, but as soon as you call FieldInfo.GetValue() it does. But that's an uneducated gut feeling for which I can't find confirmation, hence not posted as an answer.Trifurcate
offTopic but not so much, close votes should reduce reputation just like downvotes do.Ectype
The act of obtaining a value from the type through one of its static fields or properties, either using normal C# syntax to do it or reflection, counts as "referencing" in this context and will provoke the static constructor. I don't have a reference for whatever it is documenting this behavior but from observational experience this is what happens.Revis
Inspecting the Type object by itself does not provoke this constructor, nor does inspecting attributes on the fields, properties, methods, or the type itself.Revis
Sounds resonable. Now we just need to find the relevant part(s) of the specification. :) That can be tedious I know - so if people want a carrot in the form of reputation, I will put a bounty on this as soon as it becomes eligible for it (2 days from now).Riti
the question is not about finding ways to solve a specific problem Your question is off-topic here. The answer to your question, however: reflection is not covered by the C# and CLI specifications and so, it all depends on an implementation. Based on my observation (which is the same as @CodeCaster): GetField does not trigger type initializer (static constructor), but FieldInfo.GetValue does.Limes
@Limes I just reread your off-topic link. Seems very on topic to me. Perhaps it has been revised since you last read it yourself? "if your question generally covers a practical, answerable problem that is unique to software development then you’re in the right place to ask your question!" Non of the caveats following that statement applies either as far as I can tell. (But thanks for taking the time to comment, it's very helpful, and if I'm wrong I want to understand why.)Riti
...and perhaps I phrased that last part poorly. I've rephrased it now. What I mean is that I don't simply want to know if it works with a yes or no answer, but rather I want to know why it works (or doesn't work). I thought my question was sufficiently clear that no code-sample would be needed - but perhaps I'm wrong. It still doesn't change the fact that even if I add a code sample, it would fundamentally still be the same question, and thus I would expect similar or identical replies as to those I would expect this question (in its current form) to receive.Riti
The question could be about a specific problem, but it's impossible to answer that specific problem without answering the general question. Thus it would be more useful to the site to phrase the question in a matching general way, since that would make the answer much easier to find. I have actually thought this through you know. Please reply if you have time (or PM if you prefer). If you still think I'm wrong I would like to hear your opinion about it (so I can revise my own idea of how a question should look such that it better meets the needs of the many). I hope to talk to you again.Riti
Can't find it on specs, nice question. However if it's invoked by JIT compiler when building the Type Object then I'd expect to find it somewhere there. Maybe mono source code cites specsKarlik
@Riti the problem with your question, that there is no problem which you try to solve, which is off-topic. You need to ask a specific question about a specific problem. Is there any problem besides this book-worming question? What are you trying to achieve?Limes
L
1

What I really want to know is which reflection methods will trigger type initialization?

[...]

Specifically, will the two mentioned methods, GetField and GetValue, trigger type initialization if applied to static fields?

FieldInfo.GetValue triggers the type initialization. It's from the experiment observation. It all depends on an implementation, and there will be no proofs. It will not necessary work in all cases, as Reflection does not need to follow any specs, as the specifications are not covering Reflection. There are some signs that you can get a uninitialized field, but I could not produce the code to make it happen.

typeof(), Type.GetType, and Type.GetField most likely do not trigger the type initialization. But again, this is from an observation.

If you need to make sure that your type initializer will be called at/before any particular time, you need to call RuntimeHelpers.RunClassConstructor method. This is the only method which guarantees that the type initializer will be invoked and only once in a lifetime of an app domain.

What actions actually qualifies as "access"?

None of these actions, as again reflections is not covered by the specs and so these terms do not apply here.

Does getting FieldInfo metadata for a field count as "referencing" or "accessing" the field?

Neither.

It's not clear from the specification, but this is how I understand the difference between "accessing" and "referencing":

  • Access is when you invoke this member (method or property) or get/set its value (field)
  • A method references a member when the body of the method has an access expression to this member.

PS: It's still not clear what you are asking and what particular problem you try to solve.

Limes answered 5/6, 2017 at 1:29 Comment(6)
Yeah I ended up using RuntimeHelpers.RunClassConstructor. My problem I wanted to solve was ensuring that I'm not getting an uninitialized value when I call GetValue on a static field. I did end up reworking my code so that in most cases I could guarantee that the fields are initialized before I reflect on them, but there was still one case where I couldn't, and that's where I ended up using RunClassConstructor. That's it: I wanted to know that the value isn't uninitialized - but it's smarter to ask if it triggers initialization, because even normal access can return an uninitialized value.Riti
It just feels so weird that reflection would be outside the spec when the CLI specification details that the Compact Profile (which doesn't even require floating-point support) requires that "The reflection library" is present. How can you require the presence of something without specifying how that something is supposed to work..? Humorously speaking that's like saying "You have to have a Finkelding in there- I don't know what it is but it has to be in there! You won't be compliant if you ship without a Finkelding!" It sure doesn't make me happy. :(Riti
@Riti I think that you need to put this first comment and the case which did not work (and which worked) to the question to make it clearerLimes
I really only wanted to know if type initialization is triggered or not. There was nothing else I needed help with. It's convoluted, but basically a generic class has a bunch of static fields defined in a class outside my library and from a method that takes that type as a generic type parameter I needed to ensure all static fields of that type was initialized. Since I already know that accessing one static field will trigger initialization of all static fields on the same class all I needed was either a guarantee for reflection or another means of triggering it, i.e. RunClassConstructor.Riti
And to be clear it's not so much that I had a case that didn't work - rather that I had a case that wasn't guaranteed to work. Who wants to ship code that's not guaranteed to work. That doesn't sit well.Riti
Even though it's not the answer I was hoping for, maybe it's the best answer I will get - and I did promise a bounty so here you go.Riti

© 2022 - 2024 — McMap. All rights reserved.