I come from C++ and find a very different behavior between pointers in C++ and C#.
I surprisingly find this code compiles... And even... Works perfectly.
class C
{
private readonly int x = 0;
unsafe public void Edit()
{
fixed (int* p = &x)
{
*p = x + 1;
}
Console.WriteLine($"Value: {x}");
}
}
This make me very puzzled, even in C++ we have a mechanism to protect const
objects(const
in C++ almost the same thing as readonly
in C#, not const
in C#), because we don't have enough reason to arbitrarily modify const values through pointers.
Exploring further, I find there's no equivalent to C++'s low level const pointers in C#, which will be something like:
readonly int* p
in C# if it had one.
Then p can only read the object pointed to, and cannot write to it.
And for const
objects, C# banned the attempts to retrieve their address.
In C++, any attempt to modify the const
is either compile error or Undefined Behavior. In C#, I don't know whether there is any possibility that we can make use of this.
Note: In comment section: casting away const
in C++ is not related to this question, because It's valid only when the pointed to object itself is not const
, otherwise it's UB. Plus, I'm basically talking about C# and compile time behavior.
You can ask me to provide more details if you don't fully understand the question. I found most of people cannot understand this correctly, maybe it's my fault not making it clear.
I wish I could say that you only get this unexpected behaviour because of the unsafe
keyword. Unfortunately, there are at least two more ways to change that readonly field, that don't even require unsafe. You can find more about these methods in Joe Duffy's blogpost 'When is a readonly field not readonly'.
By overlapping a field with a non-readonly struct:
class C
{
private readonly int x = 0;
class other_C
{
public int x;
}
[StructLayout(LayoutKind.Explicit)]
class Overlap_them
{
[FieldOffset(0)] public C actual;
[FieldOffset(0)] public other_C sneaky;
}
public void Edit()
{
Overlap_them overlapper = new Overlap_them();
overlapper.actual = this;
overlapper.sneaky.x = 1;
Console.WriteLine($"Value: {x}");
}
}
And by accidentally aliasing this
with a ref parameter (this one is done from outside):
class C
{
private readonly int x = 0;
public C(int X)
{
x = X;
}
public void Edit(ref C foo)
{
foo = new C(1);
Console.WriteLine($"Value: {x}");
}
}
private static void Main()
{
var c = new C(0);
c.Edit(ref c);
}
All three methods are perfectly well-defined C# that you should avoid like the plague. Just imagine debugging code with complex aliasing problems stemming from outside your class. Unfortunately, there still isn't any satisfactory solution to stop aliasing problems in C#.
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