2011-07-04 42 views
7

Tôi đang đọc blog của Eric Liperts về Mutating Readonly Structs và tôi thấy nhiều tham chiếu ở đây trong SO cho blog này làm đối số tại sao các loại giá trị phải không thay đổi. Nhưng vẫn có một điều không rõ ràng, nói rằng khi bạn truy cập vào loại giá trị mà bạn luôn luôn nhận được bản sao của nó và đây là ví dụ:Các loại giá trị không thể thay đổi

struct Mutable 
{ 
    private int x; 
    public int Mutate() 
    { 
     this.x = this.x + 1; 
     return this.x; 
    } 
} 

class Test 
{ 
    public readonly Mutable m = new Mutable(); 
    static void Main(string[] args) 
    { 
     Test t = new Test(); 
     System.Console.WriteLine(t.m.Mutate()); 
     System.Console.WriteLine(t.m.Mutate()); 
     System.Console.WriteLine(t.m.Mutate()); 
    } 
} 

Và câu hỏi này là lý do tại sao khi tôi thay đổi

public readonly Mutable m = new Mutable(); 

để

public Mutable m = new Mutable(); 

tất cả mọi thứ bắt đầu làm việc es dự kiến ​​.

Vui lòng giải thích rõ hơn lý do loại giá trị phải không thay đổi. Tôi biết rằng nó là tốt cho an toàn thread, nhưng trong trường hợp này cùng có thể được áp dụng cho các loại tài liệu tham khảo.

+0

Tôi sẽ không biết cách trả lời câu hỏi này mà không cần sao chép và dán thêm các trích dẫn từ bài đăng trên blog mà bạn đã liên kết. 'm' là' readonly' là cái làm cho 't.m' là một giá trị, mà không phải là một biến được đặt trong một biến tạm thời, và nó là biến tạm thời này là' this' trong phương thức 'Mutate'. Lấy đi 'readonly', và các yếu tố còn lại không còn áp dụng nữa, vì vậy' this' trong 'Mutate' có thể là' m'. –

Trả lời

4

Các cấu trúc có phương pháp đột biến hoạt động lạ trong một số trường hợp.

Ví dụ bạn đã phát hiện là trường chỉ đọc. Một bản sao phòng thủ là cần thiết vì bạn không muốn biến đổi một trường chỉ đọc.

Nhưng cũng được sử dụng làm thuộc tính. Một lần nữa một bản sao tiềm ẩn xảy ra, và chỉ có bản sao bị đột biến. Ngay cả khi tài sản có một setter.

struct Mutable 
{ 
    private int x; 
    public int Mutate() 
    { 
     this.x = this.x + 1; 
     return this.x; 
    } 
} 

Mutable property{get;set;} 

void Main() 
{ 
    property=new Mutable(); 
    property.Mutate().Dump();//returns 1 
    property.Mutate().Dump();//returns 1 :(
} 

Điều này cho thấy các phương pháp đột biến có vấn đề với cấu trúc. Nhưng nó không cho thấy rằng một cấu trúc có thể thay đổi được với một trong hai trường công khai hoặc thuộc tính có setter là vấn đề.

2

Chủ đề an toàn là lý do kỹ thuật rõ ràng. Nó áp dụng cho các kiểu giá trị cũng như các kiểu tham chiếu (xem System.String).

Hướng dẫn chung hơn "các loại giá trị phải là bất biến" là khác nhau. Đó là về khả năng đọc mã, và chủ yếu xuất phát từ sự nhầm lẫn mà các giá trị có thể thay đổi có thể gây ra. Đoạn mã này chỉ là một ví dụ. Hầu hết mọi người sẽ không mong đợi kết quả 1,1,1.

+0

Có nhưng vẫn còn nếu nó không phải là chỉ đọc mọi thứ sẽ làm việc như mong đợi. Tổng quát hơn câu hỏi không có trong đó, nhưng đó là lý do tại sao các loại giá trị được đề xuất là không thay đổi? – NDeveloper

+0

@Ndev: Tôi nghĩ rằng tôi đã trả lời câu hỏi tổng quát hơn (Tại sao). @Damien đã phân tích mẫu mã. –

+0

nhưng không thể đọc được giống như đối với các loại ref? Hoặc bạn có nghĩa là đây là vấn đề mà họ vượt qua một tham chiếu vì vậy có thể được biến đổi .. – NDeveloper

2

Tôi không biết C# vì vậy tôi sẽ cố gắng trả lời phần thứ hai của câu hỏi của bạn.

Tại sao các loại giá trị phải không thay đổi?

Có hai loại đối tượng từ Domain Driven Design của quan điểm: đối tượng

  • giá trị/loại - danh tính của họ được xác định bởi giá trị của họ (ví dụ số: 2 luôn là 2 - một bản sắc của số 2 luôn giống nhau, vì vậy 2 == 2 luôn đúng)
  • thực thể (kiểu tham chiếu) - chúng có thể bao gồm các loại giá trị khác và danh tính của chúng được xác định bằng chính danh tính của chúng (ví dụ: mọi người là đàn ông trông giống y như bạn, nó sẽ không phải là bạn)

Nếu loại giá trị có thể thay đổi, hãy tưởng tượng điều gì có thể xảy ra nếu có thể thay đổi giá trị của số hai: 2 == 1 + 1 sẽ không được bảo đảm là đúng.

Xem những liên kết này để biết thêm:

+0

Trong khi valuetypes có thể thay đổi được vấn đề nó không phải là xấu như nó âm thanh trong câu trả lời của bạn. Vấn đề thường không phải là bạn có thể biến đổi một cái gì đó bạn không nên, nhưng bạn vô tình biến đổi một bản sao tạm thời thay vì những gì bạn muốn thay đổi. – CodesInChaos

0

Tôi nghĩ rằng điều khó khăn về ví dụ đó là người ta có thể tranh luận nó không phải là có thể. Bạn đã tạo một thể hiện của Mutable read-only và bạn có thể thay đổi giá trị của nó thông qua hàm Mutate(), do đó vi phạm khái niệm bất biến, theo nghĩa nào đó. Nói đúng ra, nó hoạt động vì trường riêng x không chỉ đọc.Nếu bạn thực hiện một thay đổi đơn giản trong lớp có thể thay đổi thì tính bất biến sẽ thực sự được thực thi:

private readonly int x; 

Sau đó, hàm Mutate() sẽ tạo ra lỗi trình biên dịch.

Ví dụ này cho thấy rõ ràng cách thức hoạt động sao chép theo giá trị trong ngữ cảnh của các biến chỉ đọc. Bất cứ khi nào bạn gọi m bạn đang tạo một bản sao của cá thể, trái ngược với một bản sao của một tham chiếu đến cá thể - cái sau sẽ xảy ra nếu Mutable là một lớp thay vì một cấu trúc.

Vì mỗi lần bạn gọi m, bạn đang gọi 1) bản sao của cá thể và 2) bản sao của cá thể chỉ đọc, giá trị của x là luôn bằng 0 tại thời điểm sao chép diễn ra. Khi bạn gọi Mutate() trên bản sao nó tăng x thành 1, hoạt động vì x chính nó không phải là chỉ đọc. Nhưng lần sau bạn gọi Mutate() bạn vẫn đang gọi nó trên giá trị mặc định ban đầu là 0. Như anh ta nói trong bài viết "m là bất biến, nhưng bản sao không phải là". Mỗi bản sao của cá thể ban đầu sẽ có x là 0 vì đối tượng được sao chép không bao giờ thay đổi trong khi các bản sao của nó có thể được thay đổi.

Có thể điều đó sẽ hữu ích.

Các vấn đề liên quan