Take a look at the example below:
struct MyStruct
{
private volatile int _field;
public void Set(int v) => _field = v;
}
class MyClass
{
private MyStruct _myStruct;
// is it same as _myStruct.Set(0)?
public void SomeMethod() => _myStruct = new MyStruct();
}
Basically the question is whether reassigning the whole struct with a volatile field inside uses the same volatile semantics as assigning the field directly.
The volatile
prefix is implemented using a modreq
compiler attribute, and is only enforced by the compiler if access is done directly through the field that is marked volatile
.
ECMA-335 specification does not actually have any volatile
attribute for fields as such, and the CLR does not enforce volatile access to such a field, so it is up to the compiler to issue a volatile.
prefix in the generated IL. It only does this if you access the field directly.
You can see this easily by decompiling the following code
public class C {
static s s1;
static s s2;
public void M() {
s1 = new s();
s2 = s1;
s2.i = s1.i;
}
}
struct s{
public volatile int i;
}
.class public auto ansi beforefieldinit C
extends [System.Private.CoreLib]System.Object
{
// Fields
.field private static valuetype s s1
.field private static valuetype s s2
// Methods
.method public hidebysig
instance void M () cil managed
{
// Method begins at RVA 0x2050
// Code size 47 (0x2f)
.maxstack 8
IL_0000: nop
IL_0001: ldsflda valuetype s C::s1
IL_0006: initobj s
IL_000c: ldsfld valuetype s C::s1
IL_0011: stsfld valuetype s C::s2
IL_0016: ldsflda valuetype s C::s2
IL_001b: ldsflda valuetype s C::s1
IL_0020: volatile.
IL_0022: ldfld int32 modreq([System.Private.CoreLib]System.Runtime.CompilerServices.IsVolatile) s::i
IL_0027: volatile.
IL_0029: stfld int32 modreq([System.Private.CoreLib]System.Runtime.CompilerServices.IsVolatile) s::i
IL_002e: ret
} // end of method C::M
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2080
// Code size 8 (0x8)
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [System.Private.CoreLib]System.Object::.ctor()
IL_0006: nop
IL_0007: ret
} // end of method C::.ctor
} // end of class C
.class private sequential ansi sealed beforefieldinit s
extends [System.Private.CoreLib]System.ValueType
{
// Fields
.field public int32 modreq([System.Private.CoreLib]System.Runtime.CompilerServices.IsVolatile) i
} // end of class s