hiểu người thiết lập tư nhân


90

Tôi không hiểu sự cần thiết phải có bộ cài đặt riêng bắt đầu bằng C # 2.

Có một phương thức setter đối với tôi là cho phép người dùng đặt một số biến trong lớp đó. Khi làm như vậy, chúng tôi sẽ không hiển thị các biến trực tiếp cho người dùng. Thay vào đó, chúng tôi để họ làm điều đó thông qua phương thức setter công khai này.

Điều này đối với tôi là sử dụng "đóng gói". Có một số lập luận khẳng định rằng bộ cài đặt riêng sẽ cho phép bạn áp dụng tính năng đóng gói.

Tôi không sử dụng tính năng đóng gói bằng cách sử dụng các phương thức setter công cộng? Tại sao chúng ta cần setters riêng?

Sự khác biệt giữa lớp không thay đổi và lớp có bộ thiết lập riêng là gì?


1
Tôi rất thích những người thiết lập tư nhân - đã giúp tôi xác định lại các lớp học xấu xí. Họ cũng làm cho nó không thể khai báo và thiết lập một biến Ví dụ không liên tục cùng một lúc như vậy: private File settingsFile = null;và sau đó trong một trong những nhà xây dựng: if (settingsFile == null) { settingsFile = GetSettingsFile() };. Refactoring code như vậy khiến tôi đôi khi khóc :). Chỉ vì bạn có thể đặt một thành viên trước hàm tạo, không có nghĩa là bạn phải, với nhiều hàm tạo, điều này làm cho việc tuân theo logic là CỨNG. Các bộ cài đặt riêng buộc bạn phải đặt các giá trị bên trong của hàm tạo hoặc mới hơn.
— Hamish Grubijan

Câu trả lời:


261

Một cách hợp lý.

Sự hiện diện của một bộ cài đặt riêng là do bạn có thể sử dụng thuộc tính tự động:

public int MyProperty { get; set; }

Bạn sẽ làm gì nếu bạn muốn làm cho nó chỉ đọc?

public int MyProperty { get; }

Chết tiệt!! Tôi không thể truy cập nó từ lớp học của mình; Tôi nên tạo nó như một thuộc tính bình thường:

private int myProperty;
public int MyProperty { get { return myProperty; } }

Rất tiếc ... nhưng tôi đã mất tính năng "Thuộc tính tự động" ...

public int MyProperty { get; private set; }

AHHH .. điều đó tốt hơn !!


Cảm ơn. Điều này làm cho cảm giác một lần nữa
— Dene

3
@ktutnik Cảm ơn bạn đã giải quyết vấn đề này theo cách bạn đã làm. Nó cũng có ý nghĩa đối với tôi, bây giờ!
— Vivek M. Chawla

3
Câu trả lời minh họa xuất sắc.
— bắt đầu

3
Oh crap!! I can't access it from my own classKhởi động C # 6.0, điều này chỉ đúng với giai đoạn khởi tạo bên ngoài. Xem câu trả lời của tôi stackoverflow.com/a/34223746/198797
— tsemer

1
Thêm vào câu trả lời của # tsemer, với c # 6, {get; }KHÔNG tương đương với { get; private set; }. Đối với cách đầu tiên property.GetSetMethod(true)trả về nullvà cách sau true. Điều này làm tôi ngạc nhiên.
— emragins

37

Một bộ cài đặt riêng rất hữu ích nếu bạn có thuộc tính chỉ đọc và không muốn khai báo biến ủng hộ một cách rõ ràng.

Vì thế:

public int MyProperty
{
    get; private set;
}

giống như:

private int myProperty;
public int MyProperty
{
    get { return myProperty; }
}

Đối với các thuộc tính không được triển khai tự động, nó cung cấp cho bạn một cách nhất quán để thiết lập thuộc tính từ bên trong lớp của bạn để nếu bạn cần xác nhận, v.v. bạn chỉ có nó ở một nơi.

Để trả lời câu hỏi cuối cùng của bạn, MSDN có điều này để nói về những người thiết lập riêng:

Tuy nhiên, đối với các lớp hoặc cấu trúc nhỏ chỉ gói gọn một tập giá trị (dữ liệu) và có ít hoặc không có hành vi, bạn nên làm cho các đối tượng không thể thay đổi bằng cách khai báo bộ truy cập đặt là riêng tư.

Từ trang MSDN trên thuộc tính Tự động triển khai


1
Tôi rất tiếc, tôi vẫn không thấy giá trị gia tăng do có bộ cài đặt riêng. Nếu chúng ta không muốn làm cho người định cư lộ ra ngoài, chúng ta chỉ có người định cư. Nếu chúng ta muốn thêm trình xác thực, chúng ta có thể có một trình xác nhận công khai và thêm xác thực vào nó. Tại sao chúng ta cần một setter không thể truy cập được? Con đường tôi hiểu nó là như "Đi xe này nhưng bạn không thể lái xe nó" tại sao bạn muốn để cho tôi chiếc xe nếu tôi sẽ không để lái xe nó anyway
— Dene

@Dene - Bạn chắc chắn có thể làm điều đó, vì nó không sai. Không bắt buộc phải có các thuộc tính được triển khai tự động.
— ChrisF

Tôi biết không có gì sai khi làm theo cách tôi đã thể hiện nó. Nó chỉ là để tôi đánh giá cao sự ngẫu hứng được thực hiện bởi C # 2. Có vẻ như có rất nhiều cường điệu xung quanh nó nhưng tôi chỉ không thể cảm nhận nó hoặc thấy giá trị.
— Dene

@Dene, tôi đã bỏ qua tất cả những lời cường điệu về nó. Nhưng, cuối cùng khi tôi thấy rằng có thể làm được điều này, tôi rất vui vì tôi đã biết cách dọn dẹp một số lớp dài dòng từ thời .Net 1.1. Mặc dù bạn có thể sử dụng giá trị của thuộc tính chưa được đặt, nhưng làm như vậy sẽ kém tự nhiên hơn so với việc sử dụng giá trị của một biến thành viên cá thể đã được đặt thành null.
— Hamish Grubijan

Nó đơn giản hóa mã. Cũng giống như thuộc tính tự động. Nhiều bản cập nhật tiếp theo cho C # nhằm làm cho mã ngắn gọn hơn và do đó có thể đọc được. Bạn vẫn có thể làm mọi thứ theo cách khác nếu muốn - khả năng tương thích ngược dường như cũng là một mục tiêu lớn. Tôi đoán đó là vấn đề về hương vị.
— niico 17/09/18

18

Nó khá đơn giản. Bộ cài đặt riêng tư cho phép bạn tạo các thuộc tính công khai hoặc được bảo vệ chỉ đọc.

Đó là nó. Đó là lý do duy nhất.

Có, bạn có thể tạo thuộc tính chỉ đọc bằng cách chỉ chỉ định getter, nhưng với các thuộc tính tự động cấy, bạn bắt buộc phải chỉ định cả get và set, vì vậy nếu bạn muốn một thuộc tính được triển khai tự động ở chế độ chỉ đọc, bạn phải sử dụng người định cư riêng. Không có cách nào khác để làm điều đó.

Đúng là các bộ cài đặt riêng không được tạo cụ thể cho các thuộc tính chỉ đọc được triển khai tự động, nhưng việc sử dụng chúng bí truyền hơn một chút vì các lý do khác, chủ yếu tập trung vào các thuộc tính chỉ đọc và sử dụng phản chiếu và tuần tự hóa.


2
Cảm ơn "nếu bạn muốn một thuộc tính được triển khai tự động ở chế độ chỉ đọc, bạn phải sử dụng bộ thiết lập riêng". Điều này làm cho ý nghĩa với tôi
— Dene

17

Với sự ra đời của C # 6.0 và cú pháp cho Bộ khởi tạo thuộc tính tự động , các bộ thiết lập riêng không còn cần thiết cho các thuộc tính chỉ được đặt khi khởi tạo, nội tuyến hoặc bên trong hàm tạo.

Các cú pháp mới này hiện được biên dịch:

Thuộc tính khởi tạo nội tuyến

public class MyClass1 {
  public string MyProperty { get; } = "Aloha!"
}

Thuộc tính khởi tạo hàm tạo

public class MyClass2 {
  public string MyProperty { get; }

  public MyClass2(string myProperty) {
    MyProperty = myProperty;
  }
}

3
@Ziggler, thực sự thì không. OP cũng không hỏi nó. Anh ta chỉ không hiểu nhu cầu có chúng. Điều này trả lời: "bạn không cần phải có chúng trong kịch bản này nữa".
— tsemer

6

Tôi không hiểu sự cần thiết phải có bộ cài đặt riêng bắt đầu bằng C # 2.

Ví dụ: lớp hóa đơn cho phép người dùng thêm hoặc xóa các mục khỏi thuộc tính Mục nhưng nó không cho phép người dùng thay đổi tham chiếu Mục (nghĩa là, người dùng không thể gán thuộc tính Mục cho một đối tượng danh sách mục khác).


public class Item
{
  public string item_code;
  public int qty;

  public Item(string i, int q)
  {
    this.item_code = i;
    this.qty = q;
  }
}

public class Invoice
{
  public List Items { get; private set; }

  public Invoice()
  {
    this.Items = new List();
  }
}

public class TestInvoice
{
  public void Test()
  {
    Invoice inv = new Invoice();
    inv.Items.Add(new Item("apple", 10));

    List my_items = new List();
    my_items.Add(new Item("apple", 10));

    inv.Items = my_items;   // compilation error here.
  }
}

+1 để làm nổi bật rằng các thuộc tính có thể được thao tác thông qua bộ thu công cộng, ngay cả khi chúng có bộ cài đặt riêng.
— user1725145

4

Ví dụ: bạn không lưu trữ biến thực tế thông qua thuộc tính hoặc sử dụng giá trị để tính toán một cái gì đó.

Trong trường hợp này, bạn có thể tạo một phương pháp để thực hiện phép tính của mình

private void Calculate(int value)
{
 //...
}

Hoặc bạn có thể làm như vậy bằng cách sử dụng

public int MyProperty {get; private set;}

Trong những trường hợp đó, tôi khuyên bạn nên sử dụng cái sau, vì thuộc tính cấu trúc lại từng phần tử thành viên nguyên vẹn.

Ngoài ra, nếu thậm chí nói rằng bạn ánh xạ thuộc tính với một Biến. Trong trường hợp như vậy, trong mã của bạn, bạn muốn viết như thế này:

public int myprop;
public int MyProperty {get { return myprop;}}

... ...

this.myprop = 30;

... ...
if(this.MyProperty > 5)
   this.myprop = 40;

Đoạn mã trên trông rất kinh khủng vì lập trình viên cần phải luôn thận trọng khi sử dụng MyProperty cho Get và myprop cho Set.

Rether để có tính nhất quán, bạn có thể sử dụng bộ cài đặt riêng để làm cho Propoerty chỉ đọc ở bên ngoài trong khi bạn có thể sử dụng bộ định vị của nó bên trong mã của mình.


3

Tôi nghĩ rằng một số người đã nhảy xung quanh điều này, nhưng đối với tôi, giá trị của các bộ cài đặt riêng là bạn có thể đóng gói hành vi của một thuộc tính, ngay cả trong một lớp. Như abhishek đã lưu ý, nếu bạn muốn kích hoạt một sự kiện đã thay đổi thuộc tính mỗi khi một thuộc tính thay đổi, nhưng bạn không muốn một thuộc tính được đọc / ghi cho công chúng, thì bạn phải sử dụng một trình cài đặt riêng hoặc bạn phải nâng sự kiện ở bất cứ đâu bạn sửa đổi trường sao lưu. Cái sau dễ bị lỗi vì bạn có thể quên. Có liên quan, nếu việc cập nhật giá trị thuộc tính dẫn đến một số phép tính được thực hiện hoặc một trường khác bị sửa đổi hoặc khởi tạo lười biếng một thứ gì đó, thì bạn cũng sẽ muốn gói nó trong bộ cài đặt riêng thay vì phải nhớ thực hiện nó ở mọi nơi bạn thực hiện sử dụng trường hỗ trợ.


2

Tính đóng gói có nghĩa là trạng thái của một đối tượng chỉ xảy ra thông qua một giao diện xác định, và do đó lớp có thể đảm bảo rằng trạng thái này luôn hợp lệ và phù hợp với mục đích của lớp.

Do đó, trong một số trường hợp, nó hoàn toàn phù hợp với nguyên tắc đóng gói để chỉ hiển thị một trường công khai - tất cả các giá trị có thể có cho trường đều hợp lệ với tất cả các giá trị có thể có khác của tất cả các trường khác và do đó lập trình viên có thể chủ động quyết định cho phép trường được thao tác tự do bởi mã bên ngoài.

Mặc dù vậy, những trường hợp này chủ yếu bị hạn chế đối với các lớp chủ yếu là "dữ liệu cũ thuần túy". Họ cũng không thú vị lắm về mặt này, vậy là đủ rồi.

Trong các trường hợp khác, bằng các ngôn ngữ khác, người ta sẽ có phương thức getter và setter, một thứ gì đó giống như int getId()để lấy một giá trị và void setId(int val)cập nhật nó.

Thuộc tính cho phép chúng ta sử dụng cùng một cú pháp để đọc và ghi thông qua các phương thức như chúng ta sẽ sử dụng để đọc và ghi một trường. Đây là một đường cú pháp tốt, mặc dù không quan trọng.

(Trên thực tế, do cách phản ánh hoạt động và các trường hợp chẳng hạn như DataBinder.Evalcó thể hữu ích để có một thuộc tính ngay cả khi một trường sẽ hoạt động tốt, nhưng đó là một vấn đề khác).

Cho đến khi các bộ cài đặt riêng được giới thiệu (thực tế, điều thay đổi với C # 2 là cú pháp để có bộ cài đặt riêng và bộ nhận công khai hoặc được bảo vệ trong cùng một khối), chúng ta có thể có một phương thức riêng để thực hiện công việc của bộ cài đặt riêng, vì vậy những người thiết lập riêng không thực sự cần thiết. Tuy nhiên, chúng rất tiện dụng, vì vậy mặc dù chỉ là đường cú pháp, nhưng chúng khá hữu ích.

Việc đóng gói không phải là vấn đề liệu bộ cài (hoặc bộ nhận) của bạn là công khai, riêng tư, được bảo vệ hay nội bộ mà là vấn đề liệu chúng có phù hợp hay không . Bắt đầu với mặc định của mọi trường là riêng tư (và cho vấn đề đó readonly) và sau đó khi cần thiết thêm các thành viên (cho dù thuộc tính hay phương thức) thay đổi các trường đó và đảm bảo rằng đối tượng vẫn hợp lệ khi chúng thay đổi . Điều này đảm bảo rằng bất biến của một lớp được giữ, có nghĩa là các quy tắc mô tả tập hợp các trạng thái hợp lệ mà nó có thể ở trong đó không bao giờ bị phá vỡ (các hàm tạo cũng giúp bằng cách đảm bảo rằng nó bắt đầu ở trạng thái hợp lệ như vậy).

Đối với câu hỏi cuối cùng của bạn, bất biến có nghĩa là một lớp không có bộ thiết lập công khai, được bảo vệ hoặc nội bộ và không có phương thức công khai, được bảo vệ hoặc nội bộ nào thay đổi bất kỳ trường nào. Có những mức độ này, trong C # có thể có ba độ:

  1. Tất cả các trường cá thể của một lớp đều là readonly, do đó, ngay cả mã riêng cũng không thể thay đổi nó. Nó được đảm bảo là không thay đổi (bất cứ thứ gì cố gắng thay đổi nó sẽ không biên dịch) và có thể tối ưu hóa có thể được thực hiện ở mặt sau của điều này.

  2. Một lớp là bất biến từ bên ngoài vì không có thành viên nào thay đổi bất kỳ điều gì, nhưng không được đảm bảo bằng cách sử dụng readonlyđể không bị thay đổi từ bên trong.

  3. Một lớp là bất biến khi nhìn từ bên ngoài, mặc dù một số trạng thái là thay đổi như một chi tiết triển khai. Ví dụ: một trường có thể được ghi nhớ, và do đó, trong khi từ bên ngoài cố gắng lấy nó chỉ lấy cùng một giá trị, lần thử đầu tiên như vậy thực sự tính toán nó và sau đó lưu trữ nó để truy xuất trong những lần thử tiếp theo.


1

Bạn cần một bộ cài đặt riêng, nếu bạn muốn hỗ trợ tình huống sau (không chỉ cho điều này, mà điều này phải chỉ ra một lý do chính đáng): Bạn có Thuộc tính chỉ đọc trong lớp của mình, tức là chỉ bản thân lớp được phép thay đổi nó, nhưng nó có thể thay đổi nó sau khi xây dựng cá thể. Đối với các ràng buộc sau đó, bạn sẽ cần phải kích hoạt sự kiện PropertyChanged, tốt hơn là điều này nên được thực hiện trong trình thiết lập thuộc tính (riêng tư). Trên thực tế, bạn chỉ có thể kích hoạt sự kiện PropertyChanged-từ một nơi khác trong lớp, nhưng sử dụng trình thiết lập riêng cho điều này là "quyền công dân tốt", vì bạn không phân phối các trình kích hoạt thay đổi tài sản của mình trên toàn bộ lớp của mình mà giữ nó ở tài sản, nơi nó thuộc về.


1

Có, bạn đang sử dụng tính năng đóng gói bằng cách sử dụng các thuộc tính, nhưng có nhiều sắc thái hơn để đóng gói hơn là chỉ kiểm soát cách các thuộc tính được đọc và ghi. Việc từ chối một thuộc tính được thiết lập từ bên ngoài lớp có thể hữu ích cho cả tính mạnh mẽ và hiệu suất.

Một lớp không thay đổi là một lớp không thay đổi sau khi nó được tạo ra, vì vậy, cần có các bộ thiết lập riêng (hoặc không có bộ thiết lập nào) để bảo vệ các thuộc tính.

Bộ cài đặt riêng được sử dụng thường xuyên hơn với thuộc tính viết tắt được cài đặt trong C # 3. Trong C # 2, bộ cài đặt thường bị bỏ qua và dữ liệu riêng được truy cập trực tiếp khi được đặt.

Thuộc tính này:

public int Size { get; private set; }

giống như:

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

ngoại trừ, tên của biến sao lưu được tạo bên trong bởi trình biên dịch, vì vậy bạn không thể truy cập trực tiếp.

Với thuộc tính viết tắt, private setter là cần thiết để tạo thuộc tính chỉ đọc, vì bạn không thể truy cập trực tiếp vào biến sao lưu.


Nếu tôi nhớ chính xác, bạn không thể có các công cụ sửa đổi quyền truy cập khác nhau trên getvà settrước C # 2.0. Ngoài ra, tôi nghĩ rằng bạn đang trộn lẫn 2.0 và 3.0, vì tốc độ viết tắt tự động triển khai mà bạn đề cập đến là 3.0.
— Anthony Pegram

1

Tôi không hiểu sự cần thiết phải có bộ cài đặt riêng bắt đầu bằng C # 2.

Ví dụ trường hợp sử dụng:

Tôi có một phiên bản của một đối tượng ứng dụng 'UserInfo'có chứa một thuộc tính SessionTokenIDV1mà tôi không muốn tiết lộ cho người tiêu dùng cùng lớp của mình.

Tôi cũng cần khả năng đặt giá trị đó từ lớp của mình.

Giải pháp của tôi là đóng gói thuộc tính như được hiển thị và đặt bộ thiết lập ở chế độ riêng tư để tôi có thể đặt giá trị của mã thông báo phiên mà không cho phép mã khởi tạo cũng đặt nó (hoặc thậm chí xem nó trong trường hợp của tôi)

public class UserInfo
{
   public String SessionTokenIDV1 { get; set; }

}


public class Example
{
  // Private vars
  private UserInfo _userInfo = new UserInfo();

  public string SessionValidV1
  {
    get { return ((_userInfo.SessionTokenIDV1 != null) && (_userInfo.SessionTokenIDV1.Length > 0)) ? "set" : "unset"; }
    private set { _userInfo.SessionTokenIDV1 = value; }
  }
}

Chỉnh sửa: Sửa thẻ mã cố định: Ví dụ có lỗi đã được sửa


-1

Tín dụng cho https://www.dotnetperls.com/property .

bộ thiết lập riêng giống như trường chỉ đọc. Chúng chỉ có thể được đặt trong phương thức khởi tạo. Nếu bạn cố gắng thiết lập từ bên ngoài, bạn sẽ gặp lỗi thời gian biên dịch.

public class MyClass
{
    public MyClass()
    {
        // Set the private property.
        this.Name = "Sample Name from Inside";
    }
     public MyClass(string name)
    {
        // Set the private property.
        this.Name = name;
    }
    string _name;
    public string Name
    {
        get
        {
            return this._name;
        }
        private set
        {
            // Can only be called in this class.
            this._name = value;
        }
    }
}

class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        Console.WriteLine(mc.name);

        MyClass mc2 = new MyClass("Sample Name from Outside");
        Console.WriteLine(mc2.name);
    }
}

Vui lòng xem ảnh chụp màn hình dưới đây khi tôi cố gắng thiết lập nó từ bên ngoài lớp học.

nhập mô tả hình ảnh ở đây

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 cookie và Chính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.