【问题标题】:Can pointers be used to modify readonly field? But why?可以使用指针来修改只读字段吗?但为什么?
【发布时间】:2020-07-16 06:58:27
【问题描述】:

我来自 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# 中,我不知道我们是否有可能利用它。

所以我的问题是:

  1. 这种行为真的很明确吗?虽然我知道在 C# 中没有像 C++ 中的 UB 这样的概念
  2. 如果是这样,我应该如何使用它?还是从不使用?

注意:在评论部分:C++ 中抛弃const 与这个问题无关,因为它只有在指向的对象本身不是const 时才有效,否则它是UB。另外,我基本上是在谈论 C# 和编译时行为。

如果您不完全理解问题,可以要求我提供更多详细信息。我发现大多数人都无法正确理解这一点,也许是我没有说清楚。

【问题讨论】:

标签: c# c++ pointers language-lawyer readonly-attribute


【解决方案1】:

我希望我可以说您之所以会出现这种意外行为是因为 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# 中没有提供“低级只读”的原因。不过接受了。
猜你喜欢
  • 2010-09-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多