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.
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
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With