2016-04-21 19 views
8

Tất cả over Stack Overflow và internet tôi thấy rằng đó là một nguyên tắc thiết kế tốt để giữ cho cấu trúc không thay đổi được. Thật không may, tôi không bao giờ thấy bất kỳ triển khai nào thực sự khiến các cấu trúc này thực sự không thay đổi được.Làm cách nào để tạo cấu trúc không thay đổi?

Giả sử rằng một cấu trúc không có bất kỳ loại tham chiếu nào bên trong nó, làm cách nào để thực sự tạo cấu trúc không thay đổi? Đó là, làm cách nào để ngăn chặn đột biến của bất kỳ trường nguyên thủy nào của nó (có lẽ do một ngoại lệ biên dịch/thời gian chạy)?

Tôi đã viết một bài kiểm tra đơn giản cố gắng tạo ra một cấu trúc bất biến, nhưng thậm chí không sử dụng System.ComponentModel.ImmutableObjectAttribute làm việc:

class Program 
{ 
    static void Main(string[] args) 
    { 
     ImmutableStruct immStruct1 = new ImmutableStruct(); 
     Console.WriteLine(immStruct1); //Before mutation. 

     immStruct1.field1 = 1; 
     immStruct1.field2 = "Hello"; 
     immStruct1.field3 = new object(); 
     Console.WriteLine(immStruct1); //After 1st mutation. 

     immStruct1.field1 = 2; 
     immStruct1.field2 = "World"; 
     immStruct1.field3 = new object(); 
     Console.WriteLine(immStruct1); //After 2nd mutation. 

     Console.ReadKey(); 
    } 
} 

[ImmutableObject(true)] 
struct ImmutableStruct 
{ 
    public int field1; 
    public string field2; 
    public object field3; 

    public override string ToString() 
    { 
     string field3String = "null"; 
     if (field3 != null) 
     { 
      field3String = field3.GetHashCode().ToString(); 
     } 
     return String.Format("Field1: {0}, Field2: {1}, Field3: {2}", field1, field2, field3String); 
    } 
} 
+0

Một chút OT, nhưng có lý do cụ thể nào bạn cần cấu trúc thay vì chỉ sử dụng lớp học không? Tôi đã không tìm thấy một trường hợp sử dụng mà một struct là thích hợp hơn một lớp học trong nhiều năm lập trình LOB. – StingyJack

+0

Đánh dấu các trường là chỉ đọc? Ngoài ra, bạn đã thấy điều này: http://stackoverflow.com/questions/29974301/should-a-c-sharp-struct-have-only-read-only-properties – Pawel

+0

Cấu trúc trong tâm trí tương tự như cấu trúc bitfield của C++. Cấu trúc được khởi tạo, và mong muốn của tôi là cấu trúc sẽ không bao giờ được thay đổi - có một lời hứa được thực hiện rằng tôi đã không sửa đổi dữ liệu một cách tình cờ. –

Trả lời

12

Làm cho các trường private readonly và vượt qua khởi đầu al đánh giá cao trong các nhà xây dựng

public struct ImmutableStruct 
{ 
    private readonly int _field1; 
    private readonly string _field2; 
    private readonly object _field3; 

    public ImmutableStruct(int f1, string f2, object f3) 
    { 
     _field1 = f1; 
     _field2 = f2; 
     _field3 = f3; 
    } 

    public int Field1 { get { return _field1; } } 
    public string Field2 { get { return _field2; } } 
    public object Field3 { get { return _field3; } } 
} 

Bắt đầu với C# 6.0 (Visual Studio 2015) Bạn có thể dùng getter chỉ tính

public struct ImmutableStruct 
{ 
    public ImmutableStruct(int f1, string f2, object f3) 
    { 
     Field1 = f1; 
     Field2 = f2; 
     Field3 = f3; 
    } 

    public int Field1 { get; } 
    public string Field2 { get; } 
    public object Field3 { get; } 
} 

Lưu ý rằng readonly lĩnh vực và getter chỉ thuộc tính có thể được khởi tạo hoặc trong các nhà xây dựng hoặc, trong lớp học, cũng với bộ khởi tạo trường hoặc thuộc tính public int Field1 { get; } = 7;.

Không đảm bảo rằng hàm tạo được chạy trên cấu trúc. Ví dụ. nếu bạn có một mảng các cấu trúc, thì bạn phải gọi các initializers một cách rõ ràng cho mỗi phần tử mảng. Đối với các mảng của các kiểu tham chiếu, tất cả các phần tử được khởi tạo lần đầu tiên là null, điều này cho thấy rõ ràng rằng bạn phải gọi new trên mỗi phần tử. Nhưng nó rất dễ dàng để quên nó cho các loại giá trị như cấu trúc.

var immutables = new ImmutableStruct[10]; 
immutables[0] = new ImmutableStruct(5, "hello", new Person()); 
+2

Trường 3 vẫn được coi là có thể thay đổi vì đây là loại tham chiếu. Bạn phải trả về một bản sao của đối tượng trong trình thu thập để giữ cho cá thể không thay đổi được. – Crowcoder

+2

Hoặc làm cho đối tượng được tham chiếu chính nó không thay đổi. 'Chuỗi' là một kiểu tham chiếu nhưng không thay đổi được. Vì các cấu trúc biểu diễn các loại giá trị, việc tham chiếu đến một loại tham chiếu có thể thay đổi có lẽ cũng không phải là một ý tưởng tốt. –

+0

Câu trả lời hay! Nó hoạt động và được cập nhật! Nhân đây, nếu bạn có một bộ khởi tạo thuộc tính, nó chỉ có thể xảy ra trong một lớp. Nếu không, bạn nhận được lỗi: '" không thể có thuộc tính instance hoặc field initializers trong cấu trúc "'. –

3

Giữ dữ liệu bất biến của bạn riêng tư:

struct ImmutableStruct 
{ 
    private int field1; 
    private string field2; 
    private object field3; 

    public ImmutableStruct(int f1, string f2, object f3) 
    { 
     field1 = f1; 
     field2 = f2; 
     field3 = f3; 
    } 

    public int Field1 => field1; 
    public string Field2 => field2; 
    public object Field3 => field3; 
} 

Hoặc ít lộn xộn:

struct ImmutableStruct 
{ 
    public ImmutableStruct(int f1, string f2, object f3) 
    { 
     Field1 = f1; 
     Field2 = f2; 
     Field3 = f3; 
    } 

    public int Field1 { get; } 
    public string Field2 { get; } 
    public object Field3 { get; } 
} 
Các vấn đề liên quan