Biến riêng vs tài sản?


41

Khi đặt giá trị cho một biến trong một lớp, hầu hết thời gian chúng ta sẽ có hai tùy chọn:

private string myValue;
public string MyValue
{
   get { return myValue; }
   set { myValue = value; }
}

Có một quy ước xác định cách chúng ta nên gán giá trị cho các biến bên trong các lớp không? Ví dụ: nếu tôi có một phương thức bên trong cùng một lớp thì tôi nên gán nó bằng thuộc tính hoặc sử dụng biến riêng. Tôi đã thấy nó được thực hiện theo cả hai cách, vì vậy tôi đã tự hỏi liệu đây là một sự lựa chọn hay hiệu suất là một yếu tố (có thể là nhỏ).

Câu trả lời:


23

Tôi sẽ tiến thêm một bước, và đưa nó đến 3 trường hợp. Mặc dù có nhiều biến thể trên mỗi loại, đây là quy tắc tôi sử dụng phần lớn thời gian khi lập trình C #.

Trong trường hợp 2 & 3, luôn luôn đi đến Trình truy cập thuộc tính (không phải biến trường). Và trong trường hợp 1, bạn được cứu khỏi thậm chí phải đưa ra lựa chọn này.

1.) Tài sản bất biến (được chuyển cho nhà xây dựng, hoặc được tạo tại thời điểm xây dựng). Trong trường hợp này, tôi sử dụng một biến trường, với thuộc tính chỉ đọc. Tôi chọn cái này trên một setter riêng, vì setter riêng không đảm bảo tính bất biến.

public class Abc
{ 
  private readonly int foo;

  public Abc(int fooToUse){
    foo = fooToUse;
  }

  public int Foo { get{ return foo; } }
}

2.) Biến POCO. Một biến đơn giản có thể nhận / đặt ở bất kỳ phạm vi công khai / riêng tư nào. Trong trường hợp này tôi sẽ chỉ sử dụng một tài sản tự động.

public class Abc
{ 
  public int Foo {get; set;}
}

3.) Thuộc tính ràng buộc ViewModel. Đối với các lớp hỗ trợ INotifyPropertyChanged, tôi nghĩ bạn cần một biến trường sao lưu riêng tư.

public class Abc : INotifyPropertyChanged
{
  private int foo;

  public int Foo
  {
    get { return foo; }
    set { foo = value;  OnPropertyChanged("foo"); }
  }
}

2
+1 cho ví dụ MVVM. Trên thực tế, đó là những gì gây ra câu hỏi ở nơi đầu tiên.
Edward

4
+1: Trộn 2/3 với AOP và bạn có cách sử dụng INPC tuyệt vời. [Thông báo] công khai int Foo {get; bộ; }
Steven Evers

1
@Job Đối với bất kỳ lớp nào truy cập vào lớp, một setter riêng là đủ cho tính bất biến. Tuy nhiên, bên trong lớp, bộ cài đặt riêng không ngăn các cài đặt lặp lại của giá trị, sau khi xây dựng ban đầu. Một tính năng ngôn ngữ như 'bộ chỉ đọc riêng tư' về mặt khái niệm có thể hoạt động xung quanh vấn đề này, nhưng nó không tồn tại.
Sheldon Warkentin

1
Tôi mới sử dụng C #, vậy hãy nói cho tôi biết, tại sao nên sử dụng public int Foo {get; set;}thay vì public int Foo?

1
Nếu một lớp hoặc struct sẽ hoạt động như một POCO hoặc PODS, thì lợi thế thực sự là gì để bao bọc các trường trong các thuộc tính? Tôi hoàn toàn hiểu rằng các trường gói trong các thuộc tính là hữu ích khi một lớp hoặc cấu trúc cần, hoặc có thể trong tương lai cần, để duy trì các bất biến đối với nội dung của nó (có lẽ bằng cách đảm bảo các đối tượng khác được cập nhật để khớp với chúng), nhưng nếu một lớp hoặc cấu trúc xác định rằng người tiêu dùng có thể viết bất kỳ giá trị nào theo bất kỳ thứ tự nào mà không hạn chế hoặc tác dụng phụ, những hành vi hữu ích nào có thể được thêm vào một người truy cập thành viên?
supercat

18

Nói chung, tôi sẽ nói gán cho trường trong hàm tạo và sử dụng thuộc tính ở mọi nơi khác. Bằng cách này, nếu ai đó thêm chức năng cho tài sản, bạn sẽ không bỏ lỡ nó ở bất cứ đâu.

Nó chắc chắn không phải là một yếu tố hiệu suất. Trình tối ưu hóa sẽ nội tuyến một get hoặc set đơn giản cho bạn và mã MSIL cuối cùng có thể sẽ giống hệt nhau.


Bất kỳ lý do cụ thể để sử dụng các trường trong các nhà xây dựng? Ít cơ hội tác dụng phụ lạ?

4
@Sign: Tôi đoán là nếu có xác nhận trên tài sản (hiện tại hoặc trong tương lai), bạn không muốn có nguy cơ xác thực thất bại trong quá trình xây dựng. Xác nhận không hợp lý ở giai đoạn này vì đối tượng không thể được đảm bảo ổn định cho đến khi hàm tạo kết thúc.
Steven Evers

@Sign: Cả những gì bạn nói và những gì Snorfus nói. Hoặc nếu tôi muốn ghi nhật ký thay đổi vào một thuộc tính, tôi có thể không muốn đăng nhập cài đặt ban đầu. Nhưng, tôi đã nói "nói chung".
pdr

3
@Sign: vấn đề là: nếu phương thức set của thuộc tính có thể bị ghi đè trong lớp con, bạn có thể có tác dụng phụ trong quá trình tạo đối tượng hoặc đối tượng không nhất quán (nghĩa là: thuộc tính bị ghi đè được lập trình để không đặt bất kỳ giá trị nào cho lĩnh vực đó). Sử dụng các thuộc tính trong constructor chỉ an toàn nếu phương thức thiết lập là riêng tư hoặc nếu lớp được niêm phong.
Diego

4

Phụ thuộc.

Trước tiên, bạn nên thích các thuộc tính tự động khi có thể:

public string MyValue {get;set;}

Thứ hai, cách tiếp cận tốt hơn có lẽ là sử dụng các thuộc tính, nếu bạn có bất kỳ logic nào ở đó, có lẽ bạn nên tự mình chuyển qua nó, đặc biệt nếu logic đó là đồng bộ hóa luồng.

Nhưng bạn cũng nên tính đến việc nó có thể cản trở hiệu suất của bạn (một chút), nếu bạn đang đồng bộ hóa không chính xác, bạn có thể tự bế tắc và đôi khi, con đường chính xác là đi vòng quanh logic trong thuộc tính.


3
Tôi cũng thích public string MyValue {get; private set;}.
Công việc

3

Chà, cách tiếp cận trực tiếp sẽ chỉ là gán nó cho chính biến đó, vì dù sao bạn đang ở trong một phương thức của lớp và bạn vẫn kiểm soát hành vi của lớp.

Nhưng toàn bộ quan điểm về các thuộc tính là chúng trừu tượng biến đi. Trong khi một thuộc tính đơn giản như trong ví dụ của bạn hoàn toàn không sử dụng chỉ một biến thành viên công cộng đơn giản, các thuộc tính thường làm (hoặc nên làm) những thứ bổ sung bên trong getters và setters của chúng. Và nếu bạn muốn những điều này được thực hiện tự động khi thay đổi thuộc tính bên trong lớp, thì tất nhiên sẽ sạch hơn khi làm việc trên thuộc tính thay vì biến để không phải thay đổi mỗi phép gán biến khi hành vi cài đặt thuộc tính thay đổi.

Bạn chỉ cần lý luận về nó về mặt khái niệm. Tài sản thực sự là một tay cầm để truy cập một số trạng thái bên trong của đối tượng, có thể bao gồm nhiều hơn một biến thành viên. Vì vậy, bạn phải tự hỏi mình nếu bạn chỉ muốn thay đổi trạng thái bên trong cơ bản (hoặc chỉ một phần của nó) hoặc thuộc tính trừu tượng đại diện cho trạng thái này, và hầu hết nó thực sự là sau vì bạn thường muốn đối tượng của mình luôn có một trạng thái nhất quán.


2

Nếu có bất kỳ cơ hội nào mà việc triển khai get / set thuộc tính đó đôi khi sẽ thay đổi sau đó (ví dụ: bạn muốn phát sinh sự kiện khi gọi sethoặc bạn sẽ thêm một số cơ chế đánh giá lười biếng sau vào getchức năng của mình ), thì đó có thể là một ý tưởng hay rằng mã của bạn trong lớp sẽ sử dụng thuộc tính trong hầu hết các trường hợp ngoại trừ các trường hợp - rất có thể hiếm gặp - trong đó bạn rõ ràng không muốn các cơ chế đánh giá sự kiện hoặc lười biếng đó được sử dụng.

Dù sao đi nữa, bất cứ điều gì bạn sẽ làm, rất có thể là khi bạn thay đổi việc triển khai tài sản sau đó theo cách đó, bạn sẽ phải xem xét tất cả các địa điểm trong lớp của bạn truy cập vào các tài sản đó để kiểm tra xem thực sự tài sản sẽ được truy cập hay biến riêng sẽ được sử dụng.


2

Tôi luôn luôn sử dụng tài sản công cộng.

Thông thường một số logic sẽ luôn chạy khi thuộc tính được đặt sẽ được thêm vào setphương thức của thuộc tính và đặt trường riêng thay vì bộ cài đặt công khai sẽ bỏ qua mọi logic ở đó.

Bạn có một nhận xét về MVVM dẫn đến câu hỏi này và tôi cảm thấy điều này thậm chí còn quan trọng hơn khi làm việc với MVVM. Nhiều đối tượng đưa ra PropertyChangethông báo bộ setter và các đối tượng khác có thể đăng ký sự kiện này để thực thi một số hành động khi các thuộc tính cụ thể thay đổi. Nếu bạn đặt biến riêng, các hành động này sẽ không bao giờ thực hiện trừ khi bạn cũng tự nâng PropertyChangedsự kiện.


+1 Có trong hầu hết các trường hợp (MVVM), sự kiện PropertyChanged là bắt buộc. Và điều đó chỉ có thể bị sa thải trong một tài sản. Lời giải thích hay.
Edward

1

Nói chung, tùy thuộc vào bạn nên làm gì với một thuộc tính và trường sao lưu của nó khi nhận / cài đặt.

Thông thường, để thống nhất giữa các mã, bạn nên sử dụng các bộ truy cập công cộng bất cứ nơi nào chúng có sẵn và phù hợp. Điều đó cho phép bạn cấu trúc lại với sự thay đổi mã tối thiểu; nếu phương thức thực hiện cài đặt này cần phải được loại bỏ khỏi lớp và đặt ở một nơi khác mà trường sao lưu không còn nữa (như lớp cơ sở), ai quan tâm? Bạn đang sử dụng thứ gì đó có sẵn ở bất cứ nơi nào có lớp để thực hiện công việc. Trường sao lưu, trong hầu hết các trường hợp, là một chi tiết thực hiện; không ai ngoài lớp học của bạn nên biết nó tồn tại.

Tình huống chính tôi có thể nghĩ đến khi bạn nên sử dụng trường sao lưu và KHÔNG phải là trình truy cập thuộc tính là khi trình truy cập có logic bổ sung (xác thực hoặc cập nhật thông tin trạng thái khác trong lớp) mà bạn không muốn chạy. Dân số ban đầu của một đối tượng là một ví dụ; bạn có thể có một lớp sử dụng hai giá trị thuộc tính để tính toán một phần ba, cũng được lưu trữ trong trường sao lưu (vì lý do kiên trì). Khi khởi tạo một bản sao mới của đối tượng được cung cấp dữ liệu từ DB, các bộ truy cập thuộc tính mà mỗi bộ tính toán lại giá trị thứ ba có thể khiếu nại nếu giá trị cần thiết khác không được đặt. Bằng cách sử dụng các trường sao lưu để đặt các giá trị ban đầu của hai (hoặc ba) thuộc tính này, bạn bỏ qua logic xác thực / tính toán cho đến khi thể hiện ở trạng thái đủ nhất quán để logic hoạt động bình thường.


0

Luôn luôn sử dụng một trong đó có ý nghĩa. Vâng, tôi biết rằng âm thanh khá giả đến mức không thể trả lời.

Điểm của các thuộc tính là cung cấp một giao diện thông qua đó bạn có thể truy cập một mô hình dữ liệu một cách an toàn. Đối với hầu hết các tình huống, bạn luôn muốn truy cập mô hình dữ liệu một cách an toàn thông qua giao diện đó, chẳng hạn như:

public Foo Bar
{
  get { return _bar; }
  set { _bar = doSomethingTo(value); }
}

Nhưng trong các tình huống khác, bạn có thể chỉ cần sử dụng một thuộc tính làm khung nhìn của mô hình dữ liệu:

public Double SomeAngleDegrees
{
  get { return SomeAngleRadians * 180 / PI; }
  set { SomeAngleRadians = value * PI / 180; }
}

Nếu nó có ý nghĩa để sử dụng các hình thức radian SomeAngle, thì bằng mọi cách sử dụng nó.

Cuối cùng, hãy chắc chắn để uống viện trợ kool của riêng bạn. Api đối mặt với công chúng của bạn nên đủ kiên cường để làm việc nội bộ.

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.