Why can't I change Type.Delimiter with Reflection?

If I changed the value of bool.TrueString , I would do it with Reflection:

 typeof(bool).GetField("TrueString", BindingFlags.Public | BindingFlags.Static).SetValue(null, "Yes"); Console.WriteLine(bool.TrueString); // Outputs "Yes" 

However, I am unable to change the value of, say, Type.Delimiter :

 typeof(Type).GetField("Delimiter", BindingFlags.Public | BindingFlags.Static).SetValue(null, '-'); Console.WriteLine(Type.Delimiter); // Outputs "." 

Why is this?

+5
source share
2 answers

I think you fall prey to the optimization that JIT does. In fact, you can change the value of this field, but for some reason, the results of this change will not be immediately visible. I managed to get around this by doing something stupid:

 typeof(Type).GetField("Delimiter", BindingFlags.Public | BindingFlags.Static).SetValue(null, '-'); Func<char> getDelimiter = () => Type.Delimiter; Console.WriteLine( getDelimiter() ); 

This code reliably showed the updated field value for me. I cannot say that I am terribly surprised; the field is declared read-only, so JITTER can use this assumption when accessing the field. You are doing something naughty and evil, there should be no expectation for this to work reasonably.

Now, why didnโ€™t this appear when changing the bool.TrueString field, I think that because bool.TrueString is a reference type ( string ), while Type.Delimiter is a value type ( char ). I could imagine that this causes various optimizations.

I looked at the disassembly for this code:

  Console.WriteLine( bool.TrueString ); 006F2E53 8B 0D B8 10 40 03 mov ecx,dword ptr ds:[34010B8h] 006F2E59 E8 52 A6 77 54 call 54E6D4B0 Console.WriteLine(Type.Delimiter); 006F2E5E B9 2E 00 00 00 mov ecx,2Eh 006F2E63 E8 B0 FA E0 54 call 55502918 

You can see quite clearly that JITTER has optimized access to the Type.Delimiter field, replacing it with the literal value '.' . Access to the static field for bool.TrueString is still loading from the actual field.

+4
source

The difference is that bool.TrueString is a reference type, and Type.Delimiter is a value type. Try using the same code for these two properties and you will see the same behavior:

 public class A { public static readonly string S = "S"; public static readonly char C = 'C'; } 

The reason for this is that the first time the method is called, the JIT compiler shows whether the static readonly value can be baked directly into the assembly code as a constant. If possible, then.

If you extract code that refers to the field, to another method, then its output will depend on whether it was first run before or after changing the value of the field. Since your code is in the same method, then it was explicitly JITted before the field value changed.

You will find that if you use a value type that can be hardcoded in a processor instruction, this JIT optimization will be applied. This includes things like int , long and char , but not things like DateTime or reference types like strings

+1
source

All Articles