2009-01-07 37 views
61

Kicking xung quanh một số cấu trúc nhỏ trong khi trả lời this post, tôi đi qua sau bất ngờ: Cấu trúc sauThuộc tính và cấu trúc tự động Không kết hợp?

, sử dụng một trường int là hoàn toàn hợp pháp:

struct MyStruct 
{ 
    public MyStruct (int size) 
    { 
     this.Size = size; // <-- Legal assignment. 
    } 

    public int Size; 
} 

Tuy nhiên, cấu trúc sau đây, sử dụng hộp số tự động thuộc tính không biên dịch:

struct MyStruct 
{ 
    public MyStruct (int size) 
    { 
     this.Size = size; // <-- Compile-Time Error! 
    } 

    public int Size{get; set;} 
} 

Lỗi trả về là "Không thể sử dụng đối tượng" này 'trước khi tất cả các trường được gán cho ". Tôi biết rằng đây là quy trình chuẩn cho một cấu trúc: trường sao lưu cho bất kỳ thuộc tính nào phải được gán trực tiếp (và không phải thông qua bộ truy cập được đặt của thuộc tính) từ bên trong hàm tạo của cấu trúc.

Một giải pháp là sử dụng một trường ủng hộ rõ ràng:

struct MyStruct 
{ 
    public MyStruct(int size) 
    { 
     _size = size; 
    } 

    private int _size; 

    public int Size 
    { 
     get { return _size; } 
     set { _size = value; } 
    } 
} 

(Lưu ý rằng VB.NET sẽ không có vấn đề này, bởi vì trong VB.NET mọi lĩnh vực sẽ được tự động khởi tạo 0/null/sai khi lần đầu tiên được tạo.)

Điều này có vẻ là một giới hạn không may khi sử dụng thuộc tính tự động với cấu trúc trong C#. Suy nghĩ về khái niệm, tôi đã tự hỏi nếu đây không phải là một nơi hợp lý để có một ngoại lệ cho phép thuộc tính thiết lập accessor được gọi trong constructor của struct, ít nhất là cho một thuộc tính tự động?

Đây là một vấn đề nhỏ, gần như một cạnh hợp cụ thể, nhưng tôi đã tự hỏi những gì người khác nghĩ về điều này ...

+2

Các trường trong C# cũng được khởi tạo thành 0/null/false. Hãy nhớ thời gian chạy của nó, không phải là ngôn ngữ cụ thể. ;) –

+0

Không dành cho các trường cấu trúc trong C#. Đối với một cấu trúc, các trường phải được khởi tạo bởi hàm dựng rõ ràng hoặc bởi người gọi nếu sử dụng hàm tạo ngầm, không có tham số. VB.NET không có giới hạn này và, do đó, ví dụ ở trên, sẽ không biên dịch trong C# sẽ biên dịch và chạy tốt trong VB.NET. –

+0

bản sao có thể có của [Tại sao cần phải gọi: this() trên cấu trúc để sử dụng các thuộc tính tự động trong C#?] (Http://stackoverflow.com/questions/272153/why-is-it-necessary-to-call -this-on-a-struct-to-use-tự động-tài sản-in-c) – nawfal

Trả lời

80

Từ C# 6 trở đi: đây không còn là một vấn đề


Trở thành C# 6, bạn cần phải gọi hàm tạo mặc định để làm việc này:

public MyStruct(int size) : this() 
{ 
    Size = size; 
} 

Một vấn đề lớn hơn ở đây là bạn có cấu trúc có thể thay đổi. Đây là không bao giờ một ý tưởng hay. Tôi sẽ làm cho nó:

public int Size { get; private set; } 

Không kỹ thuật bất biến, nhưng đủ gần.

Với phiên bản gần đây của C#, bạn có thể cải thiện về điều này:

public int Size { get; } 

này bây giờ có thể chỉ được chỉ định trong các nhà xây dựng.

+0

Cảm ơn Marc, điều này làm cho cảm giác hoàn hảo, cảm ơn bạn, tôi quên rằng chúng tôi có thể buộc khởi tạo mặc định. Và tôi đồng ý, tôi sẽ không bao giờ làm cho một cấu trúc có thể thay đổi được, nhưng tôi đã trả lời câu trả lời của người khác here. –

+4

Tôi nghĩ rằng nói * không bao giờ * là một chút mạnh mẽ. Người ta nên mặc định để bất biến, nhưng làm cho nó âm thanh như một nhiệm vụ như vậy "không bao giờ là một ý tưởng tốt." ;) –

+6

Sử dụng tuyệt đối là ** không bao giờ ** một ý tưởng tốt ;-p (xem những gì tôi đã làm ở đó ...). Thật vậy, * nếu bạn hiểu ý nghĩa *, các cấu trúc có thể thay đổi có thể sử dụng được. Vấn đề là hầu hết mọi người không hiểu họ, và nó tốn rất nhiều thời gian và sự chán nản mà họ học được. –

10

Bạn có thể khắc phục điều này bằng cách đầu tiên gọi constructor mặc định:

struct MyStruct 
{ 
    public MyStruct(int size) : this() 
    { 
     this.Size = size; // <-- now works 
    } 

    public int Size { get; set; } 
} 
7

Một công việc xung quanh che khuất cho vấn đề này là một trong những phát hiện trong lớp tạm thời Tuple trong Managed Extensibility Framework (thông qua Krzysztof Koźmic) các:

public struct TempTuple<TFirst, TSecond> 
{ 
    public TempTuple(TFirst first, TSecond second) 
    { 
     this = new TempTuple<TFirst, TSecond>(); // Kung fu! 
     this.First = first; 
     this.Second = second; 
    } 

    public TFirst First { get; private set; } 
    public TSecond Second { get; private set; } 

(Mã nguồn đầy đủ từ Codeplex: Tuple.cs)

Tôi cũng lưu ý rằng documen tation cho CS0188 đã được cập nhật thêm:

Nếu bạn gặp phải lỗi này khi cố gắng khởi tạo một tài sản trong một constructor struct , giải pháp là thay đổi tham số nhà xây dựng để xác định lĩnh vực ủng hộ thay vì chính tài sản . Tự động triển khai các thuộc tính cần tránh trong các cấu trúc vì chúng không có sự ủng hộ trường và do đó không thể là được khởi tạo theo bất kỳ cách nào từ hàm tạo .

Vì vậy, tôi lấy đó để có nghĩa là hướng dẫn chính thức là sử dụng các thuộc tính kiểu cũ trong cấu trúc của bạn khi bạn chạy vào vấn đề này, mà có lẽ ít mơ hồ (và readible hơn) so với một trong hai người kia hai lựa chọn thay thế được khám phá cho đến nay.

+0

Cảm ơn bạn đã cập nhật báo cáo lỗi. Thật vậy, tình hình hiệu quả nhất cho các cấu trúc là sử dụng một trường sao lưu rõ ràng. Nếu một người đang tìm kiếm để khởi tạo một cái gì đó khác hơn 0/null/false, sau đó điều này đòi hỏi * hai * bước nếu không có trường sao lưu: null ra và sau đó thiết lập giá trị thực tế. –

+3

Dòng "knung fu" rất tuyệt, tôi phải nói. (Không thể được thực hiện với một lớp.) Nhưng điều này cũng giống như việc có constructor được khai báo 'public TempTuple (TFirst đầu tiên, giây thứ hai): this()', mà, tôi nghĩ, là một cách sạch hơn để làm điều này . (Và chúng ta vẫn bắt đầu khởi tạo mỗi trường * hai lần *.) –

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