Sự khác biệt giữa Tài sản và Trường trong C # 3.0+


140

Tôi nhận ra rằng nó dường như là một bản sao của Sự khác biệt giữa Trường và Thuộc tính trong C # là gì? nhưng câu hỏi của tôi có một chút khác biệt (theo quan điểm của tôi):

Một khi tôi biết rằng

  • Tôi sẽ không sử dụng lớp của mình với "các kỹ thuật chỉ hoạt động trên các thuộc tính" và
  • Tôi sẽ không sử dụng mã xác nhận trong getter / setter.

Có sự khác biệt nào (ngoại trừ kiểu phát triển / kiểu phát triển trong tương lai), như một số loại kiểm soát trong việc thiết lập thuộc tính không?

Có sự khác biệt nào giữa:

public string MyString { get; set; }

public string myString;

(Tôi biết rằng, phiên bản đầu tiên yêu cầu C # 3.0 trở lên và trình biên dịch sẽ tạo các trường riêng.)


Câu trả lời:


117

Đóng gói.

Trong trường hợp thứ hai bạn vừa xác định một biến, trong trường hợp thứ nhất, có một getter / setter xung quanh biến đó. Vì vậy, nếu bạn quyết định bạn muốn xác thực biến vào một ngày sau đó - nó sẽ dễ dàng hơn rất nhiều.

Thêm vào đó họ xuất hiện khác nhau trong Intellisense :)

Chỉnh sửa: Cập nhật cho OP cập nhật câu hỏi - nếu bạn muốn bỏ qua các đề xuất khác ở đây, lý do khác là đơn giản là nó không thiết kế OO tốt. Và nếu bạn không có lý do chính đáng để làm việc đó, hãy luôn chọn một thuộc tính trên một biến / trường công khai.


9
Tại sao nó sẽ dễ dàng hơn? Điều gì ngăn cản tôi biến một trường thành một tài sản và thêm một trường sao lưu riêng tư? Điều này ảnh hưởng đến mã gọi như thế nào?
Serge Wautier 17/03/2016

30
@Serge - Nó ảnh hưởng đến mã đã được biên dịch. Ví dụ: nếu bạn đang phát triển một thư viện được sử dụng bởi một số ứng dụng, việc thay đổi một trường thành một thuộc tính trong thư viện đó sẽ yêu cầu biên dịch lại từng ứng dụng. Nếu đó là một tài sản, bạn có thể cập nhật tài sản mà không phải lo lắng.
Dustin Campbell

Tôi hoàn toàn đồng ý với bạn, tôi sử dụng tài sản luôn. Tôi chỉ tò mò về một sự khác biệt có thể có
p4bl0 17/03/2016

24
Nếu mã tiêu thụ luôn được biên dịch lại cùng lúc với lớp bị ảnh hưởng (vì vậy mọi thứ riêng tư hoặc nội bộ mà không có nội bộ hiển thị đều an toàn 100%) thì hãy biến nó thành một trường hoàn toàn OK
ShuggyCoUk 17/03/2016

1
wow Shuggy bình luận của bạn chính xác là câu trả lời tôi đang tìm kiếm!
p4bl0 17/03/2016

160

Các trường và thuộc tính trông giống nhau, nhưng chúng không phải là. Các thuộc tính là các phương thức và như vậy có một số điều không được hỗ trợ cho các thuộc tính và một số điều có thể xảy ra với các thuộc tính nhưng không bao giờ trong trường hợp các trường.

Dưới đây là danh sách các điểm khác biệt:

  • Các trường có thể được sử dụng làm đầu vào cho các out/refđối số. Thuộc tính không thể.
  • Một trường sẽ luôn mang lại kết quả tương tự khi được gọi nhiều lần (nếu chúng ta bỏ qua các vấn đề với nhiều luồng). Một tài sản như DateTime.Nowkhông phải luôn luôn bằng chính nó.
  • Các thuộc tính có thể ném ngoại lệ - các trường sẽ không bao giờ làm điều đó.
  • Các thuộc tính có thể có tác dụng phụ hoặc mất nhiều thời gian để thực thi. Các trường không có tác dụng phụ và sẽ luôn nhanh như mong đợi cho loại đã cho.
  • Các thuộc tính hỗ trợ khả năng truy cập khác nhau cho getters / setters - các trường không (nhưng các trường có thể được thực hiện readonly)
  • Khi sử dụng sự phản chiếu, các thuộc tính và các trường được coi là khác nhau MemberTypesđể chúng được định vị khác nhau ( ví dụ GetFieldsso GetPropertiesvới)
  • Trình biên dịch JIT có thể đối xử với quyền truy cập thuộc tính rất khác so với truy cập trường. Tuy nhiên, nó có thể biên dịch thành mã gốc giống hệt nhau nhưng phạm vi cho sự khác biệt là có.

11
Tuy nhiên, lưu ý rằng một vài trong số những điểm này không nên khác biệt nếu thực hành tốt được sử dụng. Đó là, các thuộc tính thực sự không bao giờ nên có tác dụng phụ, cũng không nên mất nhiều thời gian để thực thi.
Noldorin

15
@Noldorin: Tôi đồng ý, nhưng tiếc nên là từ khóa ở đây. Với các lĩnh vực mà hành vi được đảm bảo. Tôi không nói rằng bạn nên sử dụng các lĩnh vực, nhưng điều quan trọng là phải nhận thức được sự khác biệt về ngữ nghĩa.
Brian Rasmussen

4
Vâng, đủ công bằng. Các lập trình viên mới bắt đầu không có manh mối về những điều này đôi khi, thật không may ...
Noldorin

2
Ngoài ra, các trường có thể có một trình khởi tạo trường trong khi các thuộc tính phải được khởi tạo trong một hàm tạo.
Dio F

3
Tôi cảm thấy câu trả lời này là cường độ tốt hơn câu trả lời được chấp nhận. Tôi bắt đầu nghĩ rằng cách "chấp nhận được" luôn luôn ưu tiên các thuộc tính trên các trường là suy nghĩ tồi. Nếu bạn chỉ cần xử lý dữ liệu, hãy sử dụng một trường. Nếu bạn cần áp dụng chức năng cho dữ liệu, hãy sử dụng một phương pháp. Vì các thuộc tính có thể có tác dụng phụ mà bạn không biết (đặc biệt là nếu bạn không thiết kế thư viện và có ít tài liệu), chúng có vẻ phản tác dụng trực quan với tôi trong hầu hết các trường hợp.
David Peterson

41

Một vài sự khác biệt nhanh chóng, rõ ràng

  1. Một tài sản có thể có từ khóa accessor.

    public string MyString { get; private set; }
  2. Một tài sản có thể được ghi đè trong con cháu.

    public virtual string MyString { get; protected set; }

1
mmh .. nr 2 thật thú vị .. tôi đã không nghĩ về nó
p4bl0 17/03/2016

14

Sự khác biệt cơ bản là một trường là một vị trí trong bộ nhớ nơi lưu trữ dữ liệu của loại đã chỉ định. Một thuộc tính đại diện cho một hoặc hai đơn vị mã được thực thi để truy xuất hoặc đặt giá trị của loại đã chỉ định. Việc sử dụng các phương thức truy cập này được ẩn theo cú pháp bằng cách sử dụng một thành viên có vẻ như hoạt động như một trường (trong đó nó có thể xuất hiện ở hai bên của một hoạt động gán).


11

Người truy cập nhiều hơn các lĩnh vực. Những người khác đã chỉ ra một số khác biệt quan trọng và tôi sẽ thêm một điểm nữa.

Các thuộc tính tham gia vào các lớp giao diện. Ví dụ:

interface IPerson
{
    string FirstName { get; set; }
    string LastName { get; set; }
}

Giao diện này có thể được thỏa mãn theo nhiều cách. Ví dụ:

class Person: IPerson
{
    private string _name;
    public string FirstName
    {
        get
        {
            return _name ?? string.Empty;
        }
        set
        {
            if (value == null)
                throw new System.ArgumentNullException("value");
            _name = value;
        }
    }
    ...
}

Trong triển khai này, chúng tôi đang bảo vệ cả Personlớp khỏi trạng thái không hợp lệ, cũng như người gọi không nhận được null từ thuộc tính chưa gán.

Nhưng chúng tôi có thể đẩy thiết kế hơn nữa. Ví dụ, giao diện có thể không đối phó với setter. Hoàn toàn hợp pháp khi nói rằng người tiêu dùng IPersongiao diện chỉ quan tâm đến việc có được tài sản, không phải trong việc thiết lập nó:

interface IPerson
{
    string FirstName { get; }
    string LastName { get; }
}

Việc thực hiện trước đó của Personlớp thỏa mãn giao diện này. Việc nó cho phép người gọi cũng thiết lập các thuộc tính là vô nghĩa theo quan điểm của người tiêu dùng (người tiêu dùng IPerson). Chức năng bổ sung của việc triển khai cụ thể được xem xét bởi, ví dụ, người xây dựng:

class PersonBuilder: IPersonBuilder
{
    IPerson BuildPerson(IContext context)
    {

        Person person = new Person();

        person.FirstName = context.GetFirstName();
        person.LastName = context.GetLastName();

        return person;

    }
}

...

void Consumer(IPersonBuilder builder, IContext context)
{
    IPerson person = builder.BuildPerson(context);
    Console.WriteLine("{0} {1}", person.FirstName, person.LastName);
}

Trong mã này, người tiêu dùng không biết về setters tài sản - không phải là việc của anh ta để biết về nó. Người tiêu dùng chỉ cần getters, và anh ta nhận được getters từ giao diện, tức là từ hợp đồng.

Một triển khai hoàn toàn hợp lệ khác IPersonsẽ là một lớp người bất biến và một nhà máy người tương ứng:

class Person: IPerson
{
    public Person(string firstName, string lastName)
    {

        if (string.IsNullOrEmpty(firstName) || string.IsNullOrEmpty(lastName))
            throw new System.ArgumentException();

        this.FirstName = firstName;
        this.LastName = lastName;

    }

    public string FirstName { get; private set; }

    public string LastName { get; private set; }

}

...

class PersonFactory: IPersonFactory
{
    public IPerson CreatePerson(string firstName, string lastName)
    {
        return new Person(firstName, lastName);
    }
}
...
void Consumer(IPersonFactory factory)
{
    IPerson person = factory.CreatePerson("John", "Doe");
    Console.WriteLine("{0} {1}", person.FirstName, person.LastName);
}

Trong mã này, người tiêu dùng mẫu một lần nữa không có kiến ​​thức về việc điền các thuộc tính. Người tiêu dùng chỉ giao dịch với getters và triển khai cụ thể (và logic kinh doanh đằng sau nó, như thử nghiệm nếu tên trống) được để lại cho các lớp chuyên biệt - nhà xây dựng và nhà máy. Tất cả các hoạt động này là hoàn toàn không thể với các lĩnh vực.


7

Cái đầu tiên:

public string MyString {get; set; }

là một tài sản; cái thứ hai (public string MyString ) biểu thị một trường.

Sự khác biệt là, các kỹ thuật nhất định (cơ sở dữ liệu ASP.NET cho các trường hợp), chỉ hoạt động trên các thuộc tính, và không hoạt động trên các trường. Điều tương tự cũng đúng với Tuần tự hóa XML: chỉ các thuộc tính được tuần tự hóa, các trường không được tuần tự hóa.


8
Sai lầm. Tuần tự hóa XML KHÔNG tuần tự hóa các trường công cộng.
Serge Wautier 17/03/2016

2
Có lẽ. Nhưng khi bạn tạo Nguồn dữ liệu đối tượng từ một lớp, bạn chỉ có thể sử dụng các thuộc tính, không phải các trường. (Trừ khi tôi đã làm sai điều gì đó: P)
Svish 17/03/2016

Tốt là để DRY;) nhưng tôi viết một lần nữa, tôi thích vai trò mạnh mẽ của tài sản trong ngôn ngữ C #. Được triển khai tốt hơn nhiều so với Java (do đó ngay từ đầu) Nhiều, có thể tất cả các giải pháp .net chỉ hoạt động trên các thuộc tính. WPF, ASPX và nhiều hơn nữa.
Jacek Cz

3

Trong nhiều trường hợp, Thuộc tính và Trường có thể giống nhau, nhưng chúng thì không. Có những hạn chế đối với các thuộc tính không tồn tại cho các trường và ngược lại.

Như những người khác đã đề cập. Bạn có thể đặt thuộc tính chỉ đọc hoặc chỉ ghi bằng cách đặt quyền truy cập ở chế độ riêng tư. Bạn không thể làm điều đó với một lĩnh vực. Thuộc tính cũng có thể là ảo, trong khi các trường không thể.

Hãy nghĩ về các thuộc tính như đường cú pháp cho các hàm getXXX () / setXXX (). Đây là cách họ được thực hiện đằng sau hậu trường.


1

Có một sự khác biệt quan trọng khác giữa các trường và thuộc tính.

Khi sử dụng WPF, bạn chỉ có thể liên kết với các thuộc tính công cộng. Liên kết với một lĩnh vực công cộng sẽ không hoạt động. Điều này đúng ngay cả khi không thực hiện INotifyPropertyChanged(mặc dù bạn luôn luôn nên).


Tốt là để DRY;) nhưng tôi viết một lần nữa, tôi thích vai trò mạnh mẽ của tài sản trong ngôn ngữ C #. Được triển khai tốt hơn nhiều so với Java (do đó ngay từ đầu) Nhiều, có thể tất cả các giải pháp .net chỉ hoạt động trên các thuộc tính. WPF, ASPX và nhiều hơn nữa.
Jacek Cz

1

Trong số các câu trả lời và ví dụ khác, tôi nghĩ ví dụ này hữu ích trong một số tình huống.

Ví dụ: giả sử bạn có một lượt thích như sau:OnChange property

public Action OnChange { get; set; }

Nếu bạn muốn sử dụng đại biểu hơn bạn cần thay đổi nó OnChangethành fieldnhư thế này:

public event Action OnChange = delegate {};

Trong tình huống như vậy, chúng tôi bảo vệ lĩnh vực của chúng tôi khỏi sự truy cập hoặc sửa đổi không mong muốn.


0

Bạn phải luôn sử dụng các thuộc tính thay vì các trường cho bất kỳ trường công khai nào. Điều này đảm bảo rằng thư viện của bạn có khả năng thực hiện đóng gói cho bất kỳ trường nào nếu được yêu cầu trong tương lai mà không phá vỡ các mã hiện có. Nếu bạn thay thế các trường bằng các thuộc tính trong các thư viện hiện có thì tất cả mô-đun phụ thuộc sử dụng thư viện của bạn cũng cần phải được xây dựng lại.


"Luôn luôn" là một lời nói dối cho từ khó. Trong C # (tốt hơn trong Java) thuộc tính có vị trí mạnh, là (có lẽ không có ngoại lệ) chính / phương thức "ràng buộc" trong ASP, WPF và các phương thức khác. Nhưng dù sao tôi có thể tưởng tượng thiết kế với lĩnh vực không có tài sản có ý nghĩa (đôi khi)
Jacek Cz
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.