2012-02-15 27 views
17

Đây là một phiên bản đơn giản của một số mã của tôi:Cycle trong cách bố trí struct không tồn tại

public struct info 
{ 
    public float a, b; 
    public info? c; 

    public info(float a, float b, info? c = null) 
    { 
     this.a = a; 
     this.b = b; 
     this.c = c; 
    } 
} 

Vấn đề là lỗi Struct member 'info' causes a cycle in the struct layout. tôi sau struct như hành vi kiểu giá trị. Tôi có thể mô phỏng điều này bằng cách sử dụng một lớp và một chức năng thành viên bản sao, nhưng tôi không thấy lý do tại sao tôi cần phải.

Lỗi này đúng như thế nào? Sự đệ quy có lẽ có thể gây ra việc xây dựng mãi mãi trong một số tình huống tương tự, nhưng tôi không thể nghĩ ra bất kỳ cách nào có thể trong trường hợp này. Dưới đây là những ví dụ nên được tốt nếu chương trình sẽ biên dịch.

new info(1, 2); 
new info(1, 2, null); 
new info(1, 2, new info(3, 4)); 

chỉnh sửa:

Các giải pháp tôi sử dụng là làm cho "info" một lớp học thay vì một struct và cho nó một chức năng thành viên để trả lại một bản sao mà tôi sử dụng khi đi qua nó. Trong hiệu ứng mô phỏng hành vi tương tự như một cấu trúc nhưng với một lớp.

Tôi cũng đã tạo câu hỏi sau trong khi tìm kiếm câu trả lời.

Value type class definition in C#?

+0

Đoán của tôi là bạn cần có ít nhất một hàm tạo không nhận 'thông tin' làm tham số? Bạn đang sử dụng một đối số mặc định, nhưng có lẽ C# không thích điều đó. Điều gì xảy ra nếu bạn thực hiện hai nhà thầu? –

+2

Chỉ cần biến nó thành một lớp; đây không phải là dữ liệu struct –

+0

'info?' không phải là con trỏ * tới 'thông tin', đó là bản sao. Nếu bạn thực sự cần điều này (bạn không nên), tại sao không làm cho loại nullable của riêng bạn mà là một 'lớp'? Bạn thậm chí có thể có các toán tử ngầm để chuyển đổi thành 'YourNullable ' từ 'Nullable '. Tất nhiên, nó sẽ có nghĩa là một * boatload * của 'YourNullable's, khá có thể loại bỏ bất kỳ tiền thưởng (nếu có) từ việc có lớp của bạn là một' struct' :) C# 'struct' s không C 'struct' S. – Luaan

Trả lời

26

Nó không hợp pháp để có một cấu trúc có chứa chính nó như là một thành viên. Điều này là do cấu trúc có kích thước cố định và phải có kích thước tối thiểu bằng tổng kích thước của từng thành viên. Loại của bạn sẽ phải có 8 byte cho hai phao, ít nhất một byte để hiển thị có hay không info là null, cộng với kích thước của một số khác là info. Điều này tạo ra sự bất bình đẳng sau:

size of info >= 4 + 4 + 1 + size of info 

Điều này rõ ràng là không thể vì nó sẽ yêu cầu loại của bạn vô cùng lớn.

Bạn phải sử dụng loại tham chiếu (nghĩa là lớp học). Bạn có thể làm cho lớp học của bạn không thay đổi và ghi đè EqualsGetHashCode để cung cấp hành vi giống như giá trị, tương tự như lớp String.

+0

+1 lời khuyên tốt về tất cả số lượng. Bạn cũng có thể làm cho các thành viên c của đối tượng kiểu, buộc nó được đóng hộp (các nhà xây dựng vẫn có thể mất một thông tin?). Bit của một xấu xí mặc dù – MattDavey

+0

điều này thực sự làm việc cho tôi. 'thông tin công khai? c; 'không đưa ra bất kỳ lỗi nào, nó hoạt động như tham chiếu đến cấu trúc và kích thước tham chiếu là xác định được. –

10

Lý do tại sao điều này tạo ra một chu kỳ là Nullable<T> là chính nó một struct. Vì nó đề cập đến info bạn có một chu trình trong bố cục (info có một trường là Nullable<info> và nó có một trường là info). Đó là về cơ bản tương đương với sau

public struct MyNullable<T> { 
    public T value; 
    public bool hasValue; 
} 

struct info { 
    public float a, b; 
    public MyNullable<info> next; 
} 
4

Vấn đề thực sự là trên dòng này:

public info? c; 

Do đây là một struct, C# cần biết bên trong info/s bố trí trước khi nó có thể sản xuất bên ngoài info bố cục của. Và bên trong info bao gồm một bên trong bên trong info, mà lần lượt bao gồm một bên trong bên trong info, và như vậy. Trình biên dịch không thể tạo bố cục vì vấn đề tham chiếu vòng tròn này.

Lưu ý: info? c là viết tắt của Nullable<info> mà chính nó là struct.

2

Không có cách nào để đạt được ngữ nghĩa có giá trị thay đổi của các mục có kích thước thay đổi (ngữ nghĩa, tôi nghĩ rằng bạn đang theo dõi là có MyInfo1 = MyInfo2 tạo danh sách liên kết mới được tách ra khỏi danh sách bắt đầu bởi MyInfo2). Người ta có thể thay thế info? bằng một số info[] (luôn luôn là rỗng hoặc không có dân cư với mảng phần tử đơn) hoặc với lớp chủ sở hữu bao bọc một phiên bản info, nhưng ngữ nghĩa có thể không phải là thứ bạn đang theo dõi . Sau MyInfo1 = MyInfo2, các thay đổi đối với MyInfo1.a sẽ không ảnh hưởng đến MyInfo2.a cũng như không thay đổi thành MyInfo1.c ảnh hưởng đến MyInfo2.c, nhưng thay đổi thành MyInfo1.c[0].a sẽ ảnh hưởng đến MyInfo2.c[0].a. Nó sẽ là tốt đẹp nếu một phiên bản tương lai của. Net có thể có một số khái niệm về "tham khảo giá trị", để sao chép một cấu trúc sẽ không chỉ đơn giản là sao chép tất cả các lĩnh vực của nó. Có một số giá trị cho thực tế là .net không hỗ trợ tất cả các phức tạp của C++ sao chép các nhà xây dựng, nhưng cũng sẽ có giá trị trong việc cho phép lưu trữ vị trí của loại 'struct' để có một danh tính mà sẽ được liên kết với vị trí lưu trữ chứ không phải là Nội dung của nó.

Cho rằng .net hiện không hỗ trợ bất kỳ khái niệm nào, tuy nhiên, nếu bạn muốn info có thể thay đổi được, bạn sẽ phải đưa ra các ngữ nghĩa tham chiếu có thể thay đổi (bao gồm nhân bản bảo vệ) ngữ nghĩa cấu trúc-lớp-lai. Một gợi ý tôi sẽ phải nếu hiệu suất là một mối quan tâm sẽ có một lớp trừu tượng InfoBase với hậu duệ MutableInfoImmutableInfo, và với các thành viên sau:

  1. AsNewFullyMutable - Ví dụ Công - Trả về một đối tượng mới MutableInfo, với dữ liệu được sao chép từ bản gốc, hãy gọi số AsNewFullyMutable trên bất kỳ tham chiếu lồng nhau nào.

  2. AsNewMutable - Ví dụ công khai - Trả về đối tượng MutableInfo mới, với dữ liệu được sao chép từ bản gốc, gọi AsImmutable trên bất kỳ tham chiếu lồng nhau nào.

  3. AsNewImmutable - Được bảo vệ dụ - Trả về một đối tượng mới ImmutableInfo, với các dữ liệu sao chép từ orignal, gọi AsImmutable (không AsNewImmutable) trên bất kỳ tài liệu tham khảo lồng nhau.

  4. AsImmutable - Ảo công cộng - Đối với số ImmutableInfo, tự trả lại; cho số MutableInfo, hãy gọi số AsNewImmutable.

  5. AsMutable - Ảo công cộng - Đối với MutableInfo, tự trả lại; cho số điện thoại ImmutableInfo, hãy tự gọi số AsNewMutable.

Khi nhân bản một đối tượng, tùy thuộc vào việc ai ngờ rằng đối tượng hoặc hậu duệ của nó sẽ được nhân bản vô tính một lần nữa trước khi nó đã được biến đổi, người ta sẽ gọi một trong hai AsImmutable, AsNewFullyMutable, hoặc AsNewMutable. Trong các tình huống mà người ta sẽ mong đợi một đối tượng được nhân bản nhiều lần, nhân vật sẽ được thay thế bằng một thể hiện bất biến mà sau đó không còn phải được nhân bản cho đến khi có mong muốn biến đổi nó.