Можно ли использовать указатели для изменения поля только для чтения? Но почему? - PullRequest
8 голосов
/ 04 апреля 2020

Я пришел из 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 объектов (const в C ++ почти то же самое, что readonly в C#, а не const в C# ), поскольку у нас нет достаточных оснований для произвольного изменения значений констант с помощью указателей.

Исследуя далее, я обнаружил, что нет эквивалента низкоуровневых константных указателей C ++ в C#, что будет выглядеть примерно так:

readonly int* p

в C#, если он у него есть.

Тогда p может только читать указанный объект и не может писать в него.

И для const объектов C# запретил попытки получить их адрес.

В C ++ любая попытка изменить const является либо ошибкой компиляции, либо неопределенным поведением. В C# я не знаю, есть ли вероятность, что мы сможем использовать это.

Поэтому мой вопрос:

  1. Является ли это поведение действительно четким ? Хотя я знаю, что в C# нет такого понятия, как UB в C ++
  2. . Если да, то как мне его использовать? Или никогда его не использовать?

Примечание: в разделе комментариев: отбрасывание const в C ++ не связано с этим вопросом, потому что это действительно только тогда, когда сам указатель на объект не const, иначе это UB. Кроме того, я в основном говорю о C# и поведении времени компиляции.

Вы можете попросить меня предоставить более подробную информацию, если вы не полностью понимаете вопрос. Я обнаружил, что большинство людей не могут понять это правильно, возможно, я виноват в том, что не объяснил это.

1 Ответ

2 голосов
/ 08 апреля 2020

Я sh Я мог бы сказать, что такое неожиданное поведение возникает только из-за ключевого слова unsafe. К сожалению, есть по крайней мере еще два способа изменить это поле только для чтения, которые даже не требуют небезопасных. Вы можете найти больше об этих методах в блоге Джо Даффи 'Когда поле только для чтения не доступно только для чтения' .

Путем наложения поля на структуру без чтения:

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}");
    }
}

И случайно наложив на псевдоним this параметр ref (этот делается извне):

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#.

до сих пор нет удовлетворительного решения, позволяющего прекратить создавать псевдонимы.
...