Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

If I were to change the value of bool.TrueString, I'd do it using Reflection:

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

However, I cannot manage 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?

like image 584
Matias Cicero Avatar asked Aug 09 '16 20:08

Matias Cicero


1 Answers

I think you're falling prey to an optimization that's being performed by the JIT. You actually can change the value of that field but, for some reason, the results of that change won't be immediately visible. I managed to get around that 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 value of the field for me. I can't say I'm terribly surprised; the field is declared as read-only, so the JITter may use that assumption when accessing the field. You're doing something naughty and evil, there shouldn't really be any expectation for this to work in a sane way.

Now, as for why this didn't show up when modifying the bool.TrueString field, my best guess is that it's due to bool.TrueString being a reference type (string) whereas Type.Delimiter is a value type (char). I could imagine this triggering different optimizations.

I did look 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 pretty clearly that the JITter optimized away the Type.Delimiter field access by replacing it with the literal value '.'. The static field access for bool.TrueString still appear to load from the actual field.

like image 119
Kyle Avatar answered Sep 30 '22 12:09

Kyle