8

So, I've encountered a weird problem while fiddling around and learning reflection. I'm attempting to change a private, readonly field, like seen below:

public class A 
{
    private static readonly int x;

    public int X
    {
        get { return x; }
    }
}

static void Main(string[] args)
{
    A obj = new A();
    Type objType = typeof(A);

    Console.WriteLine(obj.X);

    FieldInfo objField = objType.GetField("x", BindingFlags.Static | BindingFlags.NonPublic);
    objField.SetValue(null, 100);

    Console.WriteLine(obj.X);

    Console.ReadLine();
}

If I run the program as it stands above, then 0 will be printed to console each time. However, if I comment out the first print, then the second will write out the expected 100.

Anyone who can shed some light on what's going on here? Thanks!

EDIT: Strangly it seems to work in Visual Studio 2012, but not in 2010. As far as I've found, the settings are the same in both.

EDIT 2: Works when building with platform target x64, and not with x86. Guess the new question is: why is that?

EDIT 3: Compared the x64 and x86 versions in disassembly; there seems to be some inlining going on in the x86 version.

EDIT 4: Okey, think I've figured out what's happening, sort of. I don't think that the property in class A being inlined is the problem. I believe that when it's time to read the property the second time in the main method, the property call is optimized away (backing field should be readonly, value should be the same) and the old value is reused. That's my "theory" at least.

H.S.
  • 363
  • 1
  • 10
  • thats because the first time you print you are calling it assigning it its default value which is 0,and after that its always 0,if you comment out the first,then you are assigning the value 100 and then printing its value. – terrybozzio Aug 05 '13 at 13:26
  • 1
    *Calling Marc Gravel, calling Marc Gravel* –  Aug 05 '13 at 13:27
  • Since the field is marked as `readonly` the JIT might be inlining the `X` getter. – Dustin Kingen Aug 05 '13 at 13:29
  • @terrybozzio Yeah, seems logical, but according to what I've read on this site in other threads, you should be able to change even readonly fields by using reflection. Unless I've misunderstood something – H.S. Aug 05 '13 at 13:29
  • I cannot reproduce your problem on my machine. That is, the code you presented actually *works as expected*. And just to prove I am not leading you down the garden path: http://i.imgur.com/ZR1U7vW.png – User 12345678 Aug 05 '13 at 13:32
  • @ByteBlast Guess you're the chosen one :p – H.S. Aug 05 '13 at 13:41
  • @ByteBlast Try a release build instead of a debug build. :) For me it works for a debug build and not for a release build. – Matthew Watson Aug 05 '13 at 13:44
  • @MatthewWatson Based on your comment on Romoku's answer, I already tried and the code still prints "0" and then "100" as expected. – User 12345678 Aug 05 '13 at 13:45
  • @MatthewWatson Wrong result in both release and debug – H.S. Aug 05 '13 at 13:46
  • Possibly not relevant, but why do you use a non-static property `X` to expose a **static** field `x`? Have you tested if marking the field `volatile` changes anything? I'm not sure it's relevant with just one thread, but some optimization might choose to read `x` "too early" because the readonlyness seems to indicate that it is OK. ***Edit:*** The compiler does not allow `volatile` together with `readonly`, so part of this comment is meaningless. – Jeppe Stig Nielsen Aug 05 '13 at 15:00
  • If you remove the _first_ line with `Console.WriteLine(obj.X);`, then the last `Console.WriteLine(obj.X);` gets it right, i.e. `100`. – Jeppe Stig Nielsen Aug 05 '13 at 15:12
  • @JeppeStigNielsen Long story, but mostly I'm just playing around with reflection at this point. I might use it later (after I'm certain it works) to emulate a per-type unique ID for components in my entity-component game engine project. Currently I'm using a Dictionary to get unique IDs for my component types, but with some fairly ugly hacks, I can make it into a property call instead, making it way faster. EDIT: And yeah, I know, I mentioned that in the first post. – H.S. Aug 05 '13 at 15:21
  • If you change the type of the field and property to e.g. `string` (a reference type) or `System.Numerics.BigInteger` (a value type which holds a reference to an array instance), the "problem" goes away. – Jeppe Stig Nielsen Aug 05 '13 at 15:26
  • @JeppeStigNielsen You're right. Peculiar – H.S. Aug 05 '13 at 16:01

1 Answers1

6

The JIT is inlining the getter:

Use the MethodImplAttribute on the getter to suggested the JIT to not inline the property. This will not prevent the value of x from getting inlined, but removing the readonly will produce the desired result.

public class A
{
    private static int x;

    public int X
    {
        [MethodImpl(MethodImplOptions.NoInlining)]
        get { return x; }
    }
}

Now the output will be:

0
100

See: Does C# inline properties?

One simple workaround is to use the nullable integer (int?) value type.

public class A 
{
    private static readonly int? x;

    public int X
    {
        get
        {
            return x ?? 0;
        }
    }
}

x will not get inlined since the CLR will need to check whether the value of x is a valid cast to int.

Community
  • 1
  • 1
Dustin Kingen
  • 20,677
  • 7
  • 52
  • 92
  • You can see different results when you compile a debug build instead of a release even after making that change. I feel sure that it must be *something* to do with the jitter, though. – Matthew Watson Aug 05 '13 at 13:32
  • 1
    I still get the same result; both with deriving from MarshalByRefObject and with the attribute. – H.S. Aug 05 '13 at 13:44
  • Above code still reproduces the "problem" under the condition specified in the EDIT paragraphs of the question, so `NoInlining` in attribute of `get` accessor does not seem to be enough. – Jeppe Stig Nielsen Aug 05 '13 at 15:16
  • The static field is `readonly` so the JIT inlines the property call. The solution would be don't change a static field that is marked as `readonly` since there is no way to control the JIT behavior. – Dustin Kingen Aug 05 '13 at 17:28