我来自C++,发现C++和C#中的指针之间有非常不同的行为。
我惊讶地发现此代码可以编译...甚至...完美地运行。
class C
{
private readonly int x = 0;
unsafe public void Edit()
{
fixed (int* p = &x)
{
*p = x + 1;
}
Console.WriteLine($"Value: {x}");
}
}
这让我感到非常困惑,即使在C++中,我们也有一种机制来保护
const
对象(C++中的const
与C#中的readonly
几乎相同,而不是C#中的const
),因为我们没有足够的理由通过以下方式任意修改const值:指针。进一步研究,我发现在C#中没有C++的低级const指针,它类似于:
readonly int* p
在C#中,如果有的话。
然后,p只能读取指向的对象,而不能写入它。
对于
const
对象,C#禁止尝试检索其地址。 在C++中,任何尝试修改
const
的尝试都是编译错误或未定义行为。在C#中,我不知道我们是否有可能利用它。所以我的问题是:
注意:在注释部分:在C++中丢弃
const
与该问题无关,因为仅当指向的对象本身不是const
时才有效,否则为UB。另外,我基本上是在谈论C#和编译时行为。如果您不完全理解该问题,可以要求我提供更多详细信息。我发现大多数人都无法正确理解这一点,也许这是我的不明确之处。
最佳答案
我希望可以说,由于unsafe
关键字,您只会得到这种意外的行为。不幸的是,至少还有另外两种更改该只读字段的方法,它们甚至不需要不安全的方法。您可以在Joe Duffy的博客文章'When is a readonly field not readonly'中找到有关这些方法的更多信息。
通过将字段与非只读结构重叠:
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}");
}
}
并通过使用ref参数意外地为
this
别名(这是从外部完成的):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);
}
这三种方法都是定义明确的C#,您应该避免像瘟疫一样避免使用C#。试想一下,调试代码具有类外产生的复杂别名问题。不幸的是,仍然没有任何令人满意的解决方案来停止C#中的别名问题。
关于c# - 可以使用指针来修改只读字段吗?但为什么?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/61023196/