Thuộc tính .NET - Sử dụng thuộc tính riêng tư hoặc thuộc tính ReadOnly?


45

Trong tình huống nào tôi nên sử dụng Tập riêng trên một thuộc tính so với biến nó thành thuộc tính ReadOnly? Hãy xem xét hai ví dụ rất đơn giản dưới đây.

Ví dụ đầu tiên:

Public Class Person

    Private _name As String

    Public Property Name As String
        Get
            Return _name
        End Get
        Private Set(ByVal value As String)
            _name = value
        End Set
    End Property

    Public Sub WorkOnName()

        Dim txtInfo As TextInfo = _
            Threading.Thread.CurrentThread.CurrentCulture.TextInfo

        Me.Name = txtInfo.ToTitleCase(Me.Name)

    End Sub

End Class

// ----------

public class Person
{
    private string _name;
    public string Name
    {
        get { return _name; }
        private set { _name = value; }
    }

    public void WorkOnName()
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        this.Name = txtInfo.ToTitleCase(this.Name);
    }
}

Ví dụ thứ hai:

Public Class AnotherPerson

    Private _name As String

    Public ReadOnly Property Name As String
        Get
            Return _name
        End Get
    End Property

    Public Sub WorkOnName()

        Dim txtInfo As TextInfo = _
            Threading.Thread.CurrentThread.CurrentCulture.TextInfo

        _name = txtInfo.ToTitleCase(_name)

    End Sub

End Class

// ---------------

public class AnotherPerson
{
    private string _name;
    public string Name
    {
        get { return _name; }
    }

    public void WorkOnName()
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        _name = txtInfo.ToTitleCase(_name);
    }
}

Cả hai đều mang lại kết quả như nhau. Đây có phải là một tình huống không có đúng sai và đó chỉ là vấn đề ưu tiên?


public string Name { get; protected set; }thông qua thừa kế.
samis

Câu trả lời:


42

Có một vài lý do để sử dụng private set.

1) Nếu bạn hoàn toàn không sử dụng trường sao lưu và muốn có thuộc tính tự động chỉ đọc:

public string Name { get; private set; }   

public void WorkOnName()
{
    TextInfo txtInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
    Name = txtInfo.ToTitleCase(Name);
}  

2) Nếu bạn muốn thực hiện thêm công việc khi bạn sửa đổi biến trong lớp của bạn và muốn nắm bắt nó ở một vị trí:

private string _name = string.Empty;
public string Name 
{ 
    get { return _name; }
    private set 
    {
        TextInfo txtInfo = Thread.CurrentThread.CurrentCulture.TextInfo;
        _name = txtInfo.ToTitleCase(value);
    }
}

Tuy nhiên, nói chung, đó là vấn đề sở thích cá nhân. Theo tôi biết, không có lý do hiệu suất để sử dụng cái này hơn cái kia.


1
Chỉ cần thêm điều này bởi vì câu hỏi cũng có thẻ vb.net, nhưng trong vb.net bạn cần chỉ định một người ủng hộ nếu bạn sử dụng riêng tư trên cả get hoặc set. Vì vậy, trong vb.net tôi thực sự ít làm việc để làm cho tài sản chỉ đọc.
dùng643192

Tôi chưa bao giờ biết về điều đó private set. :-)
Afzaal Ahmad Zeeshan

9
Một bản cập nhật cho những người đọc câu trả lời này vào năm 2016. C # 6.0 đã giới thiệu các thuộc tính tự động chỉ đọc, cho phép bạn có một thuộc tính chỉ đọc mà không cần trường sao lưu : public string Name { get; }. Nếu bạn không muốn một thuộc tính có thể thay đổi, đó là cú pháp ưa thích ngay bây giờ.
Alexey

4
Một lý do rất tốt để không sử dụng private setlà nó không bất biến như chúng ta muốn giả vờ. Nếu bạn muốn thực hiện một lớp học thực sự bất biến, chỉ đọc là phải.
RubberDuck

Có thể là một lý do hiệu suất để KHÔNG sử dụng chỉ đọc. Có vẻ gây ra sự sao chép không cần thiết của các cấu trúc khi truy cập các phương thức của trường cấu trúc chỉ đọc. codeblog.jonskeet.uk/2014/07/16/ cường
Triynko

28

Sử dụng thiết lập riêng tư khi bạn muốn setter không thể được truy cập từ bên ngoài .

Sử dụng chỉ đọc khi bạn muốn đặt thuộc tính chỉ một lần . Trong hàm tạo hoặc bộ khởi tạo biến.

KIỂM TRA NÀY:

void Main()
{
    Configuration config = new Configuration();
    config.ResetConfiguration();

    ConfigurationReadOnly configRO = new ConfigurationReadOnly();
    configRO.ResetConfiguration();
}

public class Configuration
{
    public Color BackgroundColor { get; private set; }
    public Color ForegroundColor { get; private set; }
    public String Text { get; private set; }

    public Configuration()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }

    public void ResetConfiguration()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }
}

public class ConfigurationReadOnly
{
    public readonly Color BackgroundColor;
    public readonly Color ForegroundColor;
    public readonly String Text;

    public ConfigurationReadOnly()
    {
        BackgroundColor = Color.Black;
        ForegroundColor = Color.White;
        Text = String.Empty;
    }

    public void ResetConfiguration()
    {
        BackgroundColor = Color.Black; // compile error: due to readonly keyword
        ForegroundColor = Color.White; // compile error: due to readonly keyword
        Text = String.Empty; // compile error: due to readonly keyword
    }
}

Trong khi tôi đồng ý với câu trả lời của bạn, ví dụ của bạn có thể sử dụng một số cải tiến. Bạn có thể muốn đưa ra một nhận xét trong đó lỗi trình biên dịch sẽ xảy ra.
Michael Richardson

NB Cú pháp VB.NET tương ứng với readonlytừ khóa C # , là áp dụng ReadOnlycho trường thay vì cho thuộc tính.
Zev Spitz

8

Tôi có thể đề nghị một lựa chọn thứ ba?

public class Person
{
    public string Name { get; protected set; }

    public void SetName(string name)
    {
        TextInfo txtInfo = System.Threading.Thread.CurrentThread.CurrentCulture.TextInfo;
        this.Name = txtInfo.ToTitleCase(name);
    }
}

Điều này làm cho thuộc tính Name chỉ đọc hiệu quả đối với tất cả các mã bên ngoài và cung cấp phương thức Set rõ ràng. Tôi thích Tập rõ ràng hơn là chỉ sử dụng tập trên thuộc tính Tên vì bạn đang thay đổi giá trị khi đặt. Thông thường nếu bạn thiết lập một giá trị bất động sản, bạn mong đợi để có được trở lại giá trị tương tự khi bạn gọi get sau này, đó sẽ không xảy ra nếu bạn đã làm ToTitleCase của bạn trong bộ .

Tuy nhiên, như bạn đã nói, không có ai trả lời đúng.


Tôi tin rằng 'tập riêng' có ngữ nghĩa đặc biệt trong trình biên dịch (không chỉ đơn giản là hoạt động như một trình truy cập riêng). Đây cũng là trường hợp với bộ được bảo vệ? Nếu không, thì ngữ nghĩa tương đương với tập được bảo vệ nếu tập riêng có ngữ nghĩa đặc biệt? Tôi đã không thể tìm thấy bất kỳ tài liệu giải thích điều này.
Sprague

1
+1 nhưng tôi sẽ gọi phương thức là "Đổi tên" thay vì "SetName".
MattDavey


4

Đừng sử dụng ví dụ thứ hai. Toàn bộ quan điểm của việc sử dụng một thuộc tính - ngay cả khi không có gì xảy ra ngoài cài đặt getter và cài đặt setter - là chuyển tất cả quyền truy cập thông qua getter và setter đó để nếu bạn cần thay đổi hành vi trong tương lai, tất cả sẽ vào một nơi.

Ví dụ thứ hai của bạn từ bỏ rằng trong trường hợp thiết lập thuộc tính. Nếu bạn đã sử dụng phương pháp đó trong một lớp lớn, phức tạp và sau đó cần thay đổi hành vi của tài sản, bạn sẽ ở trong vùng đất tìm kiếm và thay thế, thay vì thực hiện thay đổi ở một nơi - nơi đặt riêng.


2

Bất cứ khi nào tôi cần thay đổi cấp độ truy cập của trình thiết lập, tôi thường thay đổi nó thành Được bảo vệ (chỉ lớp này và các lớp dẫn xuất mới có thể thay đổi giá trị) hoặc Bạn bè (chỉ các thành viên trong hội đồng của tôi mới có thể thay đổi giá trị).

Nhưng sử dụng Private có ý nghĩa hoàn hảo khi bạn muốn thực hiện các tác vụ khác trong setter bên cạnh việc thay đổi giá trị sao lưu. Như đã chỉ ra trước đó, thiết kế tốt để không tham chiếu trực tiếp các giá trị sao lưu của bạn mà thay vào đó chỉ truy cập chúng thông qua các thuộc tính của chúng. Điều đó đảm bảo rằng những thay đổi sau này bạn thực hiện đối với một tài sản được áp dụng nội bộ cũng như bên ngoài. Và hầu như không có hình phạt hiệu suất nào khi tham chiếu một thuộc tính so với biến hỗ trợ của nó.


0

Và hầu như không có hình phạt hiệu suất ...

Tuy nhiên, để làm rõ, truy cập vào một tài sản chậm hơn so với truy cập biến sự ủng hộ của mình. Getter và setter của một thuộc tính là các phương thức yêu cầu Gọi và Trả về, trong khi biến hỗ trợ của thuộc tính được truy cập trực tiếp.

Đó là lý do tại sao, trong trường hợp getter của một thuộc tính có thể được truy cập nhiều lần trong một khối mã, giá trị của thuộc tính đôi khi được lưu trữ trước tiên (được lưu trong một biến cục bộ) và biến cục bộ được sử dụng thay thế. Tất nhiên, giả định rằng tài sản không thể được thay đổi không đồng bộ trong khi khối đang thực thi.

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.