Con trỏ có thể được sử dụng để sửa đổi trường chỉ đọc? Nhưng tại sao?


8

Tôi đến từ C ++ và tìm thấy một hành vi rất khác nhau giữa các con trỏ trong C ++ và C #.

Tôi ngạc nhiên thấy mã này biên dịch ... Và thậm chí ... Hoạt động hoàn hảo.

class C
{
    private readonly int x = 0;
    unsafe public void Edit()
    {
        fixed (int* p = &x)
        {
           *p = x + 1;
        }
        Console.WriteLine($"Value: {x}");
    }
}

Điều này làm tôi rất bối rối, ngay cả trong C ++, chúng tôi có một cơ chế để bảo vệ constcác đối tượng ( consttrong C ++ gần giống như readonlytrong C #, không phải consttrong C #), vì chúng tôi không có đủ lý do để tự ý sửa đổi giá trị const thông qua con trỏ.

Khám phá thêm, tôi thấy không có tương đương với con trỏ const cấp thấp của C ++ trong C #, sẽ giống như:

readonly int* p

trong C # nếu nó có một.

Sau đó p chỉ có thể đọc đối tượng được trỏ đến, và không thể ghi vào nó.

Và đối với constcác đối tượng, C # đã cấm các nỗ lực truy xuất địa chỉ của họ.

Trong C ++, mọi nỗ lực sửa đổi constđều là lỗi biên dịch hoặc Hành vi không xác định. Trong C #, tôi không biết liệu có khả năng nào chúng ta có thể sử dụng điều này không.

Vì vậy, câu hỏi của tôi là:

  1. Là hành vi này thực sự được xác định rõ? Mặc dù tôi biết trong C # không có khái niệm như UB trong C ++
  2. Nếu vậy, tôi nên sử dụng nó như thế nào? Hoặc không bao giờ sử dụng nó?

Lưu ý: Trong phần bình luận: bỏ đi consttrong C ++ không liên quan đến câu hỏi này, bởi vì nó chỉ hợp lệ khi bản thân đối tượng chỉ không phải là đối tượng const, nếu không thì đó là UB. Thêm vào đó, về cơ bản tôi đang nói về C # và biên dịch hành vi thời gian.

Bạn có thể yêu cầu tôi cung cấp thêm chi tiết nếu bạn không hiểu đầy đủ câu hỏi. Tôi thấy hầu hết mọi người không thể hiểu điều này một cách chính xác, có thể đó là lỗi của tôi không làm cho nó rõ ràng.


Bình luận không dành cho thảo luận mở rộng; cuộc trò chuyện này đã được chuyển sang trò chuyện .
Samuel Liew

Câu trả lời:


2

Tôi ước tôi có thể nói rằng bạn chỉ nhận được hành vi bất ngờ này vì unsafetừ khóa. Thật không may, có ít nhất hai cách nữa để thay đổi trường chỉ đọc đó, thậm chí không yêu cầu không an toàn. Bạn có thể tìm hiểu thêm về các phương pháp này trong blogpost của Joe Duffy 'Khi nào là trường chỉ đọc không chỉ đọc' .

Bằng cách chồng lấp một trường với cấu trúc không chỉ đọc:

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

Và bằng cách vô tình răng cưa thisvới một tham số ref (cái này được thực hiện từ bên ngoài):

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

Tất cả ba phương pháp đều được xác định hoàn hảo C # mà bạn nên tránh như bệnh dịch hạch. Chỉ cần tưởng tượng mã gỡ lỗi với các vấn đề răng cưa phức tạp xuất phát từ bên ngoài lớp học của bạn. Thật không may, vẫn không có bất kỳ giải pháp thỏa đáng nào để ngăn chặn các vấn đề răng cưa trong C #.


1
Điều này đã trả lời câu hỏi của tôi, tuy nhiên nó vẫn không cho tôi lý do tại sao "mức độ chỉ đọc thấp" không được cung cấp trong C #. Được chấp nhận, mặc dù.
John Đinh
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.