Bạn có nên làm tài sản riêng?


19
private string mWhatever;

private string Whatever
{
    get
    {
        return this.mWhatever;
    }
    set
    {
        this.mWhatever = value;
    }
}

Tôi đã thấy một số người tạo tài sản cho từng thành viên, riêng tư hay không ... điều này có ý nghĩa gì không? Tôi có thể thấy nó có ý nghĩa trong 1% các trường hợp khi bạn muốn kiểm soát quyền truy cập vào thành viên bên trong lớp có chứa nó bởi vì nếu bạn không sử dụng thuộc tính cho mọi thành viên, điều đó sẽ dẫn đến sự không nhất quán và kiểm tra xem thành viên có quyền truy cập hay không (vì bạn có quyền truy cập vào cả hai trong phạm vi của lớp).

Câu trả lời:


17

Câu trả lời ngắn gọn: , khi có nhu cầu. Nếu không, hãy sử dụng trình nhận và cài đặt thuộc tính được triển khai tự động nhưprivate string Whatever { get; set;}

  • Nó rất tiện dụng Khi bạn đang sử dụng một cách tiếp cận tên miền gần
  • Nó cũng hữu ích khi kiểm tra một logic cụ thể khi bạn đang đặt giá trị

Dưới đây là một mô tả đầy đủ về thời điểm bạn sẽ sử dụng setters riêng: Sử dụng thuộc tính C # .


Nó cũng thực sự dễ dàng để công khai tài sản sau này nếu cần.
Tom Pickles

4
Một cách tiếp cận tên miền gần là gì?
Trisped

1
Tôi đã đề cập đến một setter riêng trong một thuộc tính của lớp. Nó nên ngăn truy cập trực tiếp để đặt giá trị từ đối tượng và tạo điều kiện cho một số kiểm tra logic trước khi đặt giá trị.
EL Yusubov

có các biến riêng tư với setter / getters công khai (ngay cả khi chúng chỉ ngây thơ) cũng có lợi thế là nếu mã của bạn được biên dịch vào thư viện, chữ ký sẽ không thay đổi nếu bạn thay thế setter / getters ngây thơ của bạn bằng các setter hiệu ứng, v.v. nhưng nếu bạn sử dụng một biến công khai đơn giản thay vào đó, chữ ký thư viện sẽ thay đổi nếu bạn thay đổi nó thành một setter / getters không ngây thơ riêng tư. .. điều đó có nghĩa là nếu thư viện của bạn thay đổi theo cách đó thì người tiêu dùng của thư viện của bạn sẽ cần phải biên dịch lại.
orion elenzil

12

Một số câu trả lời tốt ở đây đã có, nhưng tôi nghĩ rằng hầu hết trong số họ bỏ lỡ một điểm trong câu hỏi của bạn:

Tôi đã thấy một số người tạo tài sản cho từng thành viên, riêng tư hay không ... điều này có ý nghĩa gì không?

Tôi nghĩ rằng điều này hiếm khi cần thiết trước đó, cho mỗi thành viên . Bắt đầu với

 private string Whatever;

Khi bạn đến điểm cần đóng gói hoặc điểm dừng có điều kiện cho thành viên cụ thể này, bạn vẫn có thể thay thế nó bằng một thuộc tính có cùng tên - trong hầu hết các trường hợp mà không thay đổi mã sử dụng Whatever. Nhưng hãy cẩn thận, có những khác biệt tinh tế, xem câu trả lời của Brian Rasmussen trong bài viết SO này (liên kết cũng được đưa ra bởi Emmad Kareem, +1).


+1, bạn cũng đúng, hầu hết các câu trả lời đều bỏ lỡ các điểm câu hỏi ban đầu, chúng tôi là những người làm CNTT điển hình :)
NoChance

Tái cấu trúc một lĩnh vực cho một tài sản sau khi thực tế là một cơn ác mộng; yêu cầu bạn biên dịch lại tất cả người tiêu dùng. Thay đổi một thuộc tính tự động để chứa logic tùy chỉnh là một cách dễ dàng. Nếu thậm chí có một chút khả năng mã nhìn thấy bộ tái cấu trúc, thì nó sẽ hoạt động ít hơn rất nhiều nếu bạn chỉ sử dụng các thuộc tính từ lúc khởi phát.
Dan

@Dan: trong hầu hết các dự án tôi đã thấy nỗ lực là khá giống nhau, bạn phải biên dịch lại trong cả hai trường hợp. Tuy nhiên, tôi nghĩ rằng bạn đã đúng rằng việc sử dụng các thuộc tính, đặc biệt là các thuộc tính tự động, có thể giúp tránh một số vấn đề phụ với sự phản chiếu hoặc việc sử dụng các biến thành viên trong các tham số "ngoài".
Doc Brown

@DocBrown Trong mọi trường hợp, đó chắc chắn không phải là một tuyên bố bắt tất cả. Có rất nhiều lý do để chỉ đơn giản và sử dụng một lĩnh vực. Nếu bạn có thể dự đoán một bộ tái cấu trúc, ngay cả những vấn đề tinh tế cũng là những vấn đề đáng để xem xét.
Dan

7

Một trong những lợi thế lớn nhất của phương pháp này là bạn có quyền kiểm soát khi các biến của bạn thay đổi .

Hãy xem xét những điều sau đây.
Bạn đang gỡ lỗi một dự án lớn. Một phương pháp nhất định ném một ngoại lệ. Bạn đặt một điểm dừng và bạn tiết lộ rằng một biến thành viên nhất định có giá trị sai. Giả sử mã của bạn phụ thuộc rất nhiều vào biến này và bạn tìm thấy hàng trăm cách viết ( kịch bản Spaghetti ). Vì vậy, bạn không thể tìm ra được những tập quán gán một giá trị xấu.
Nếu mã đa luồng, gỡ lỗi có thể trở thành một cơn ác mộng thực sự.

Với thuộc tính, bạn có thể đặt điểm dừng trong setter . Kết hợp với một điều kiện, nó có thể bị đóng đinh trong một lần chạy.

VC ++ có các điểm dừng dữ liệu , nhưng nó chỉ có sẵn cho mã không được quản lý.


1

Nếu bạn không sử dụng thuộc tính, lựa chọn duy nhất khác của bạn là sử dụng một trường để lưu trữ dữ liệu biến. Thuộc tính và các lĩnh vực có sự khác biệt đáng kể. Hầu hết những khác biệt này được tìm thấy Trường so với Thuộc tính . Tôi đề nghị bạn nghiên cứu sự khác biệt sau đó đưa ra lựa chọn theo nhu cầu ứng dụng của bạn. Tuy nhiên, hãy nhớ rằng sử dụng các trường thay vì các thuộc tính không phải là cách thực hành OO phổ biến vì bản chất và thiếu sót của chúng.


1

Các thuộc tính riêng tư có thể rất hữu ích để gói gọn hành vi của những thứ bên trong lớp của bạn. Chỉ vì chúng là riêng tư không có nghĩa là bạn không nên tận dụng lợi thế của đường cú pháp mà các đặc tính mang lại cho bạn.


Ví dụ đưa ra là một người nghèo, tuy nhiên. Getters tài sản nồi hơi và setters như thế này hầu như luôn luôn là một ý tưởng tồi. Nếu bạn đang sử dụng C # 3.0 trở lên thì thuộc tính tự động là một ý tưởng tốt hơn nhiều:

private string Whatever { get; set; };

Điều này là ngắn hơn, sạch hơn, và dễ đọc hơn nhiều. Trong thực tế, nó chỉ dài hơn việc khai báo biến dự phòng.

Quan trọng nhất là, các thuộc tính tự động có thể được chuyển đổi thành các thuộc tính đầy đủ bất cứ lúc nào mà không thay đổi ngữ nghĩa của phần còn lại của chương trình! Do đó, nếu bạn cần thêm xác nhận hoặc xử lý lỗi, bạn có thể thực hiện dễ dàng.


Vì vậy, tôi luôn nghĩ rằng thật đáng tiếc khi bạn không thể có một thuộc tính chỉ đọc, để thực thi chỉ có thể ghi vào giá trị trong hàm tạo, (tôi thích các loại không thay đổi ) nhưng điều này đã được thêm vào trong C # 6.0 như "Trình khởi tạo thuộc tính tự động", cho phép bạn thực hiện:

private string Whatever { get; } = ...;

hoặc là

private string Whatever { get; };

cùng với

    Whatever = ...;

trong các nhà xây dựng.


0

Không nhất thiết phải làm điều này cho các thuộc tính riêng tư, nhưng nó cho phép sửa đổi cách thuộc tính nhận / đặt giá trị cơ bản mà không thay đổi cách truy cập.

Ví dụ: sửa đổi cách đặt giá trị cơ bản:

private string Whatever
{
    get
    {
        return this.mWhatever;
    }
    set
    {
        this.mWhatever = string.IsNullOrEmpty(value) ? string.Empty : value;
    }
}

0

Vâng

Cá nhân tôi sử dụng nó như là cơ chế lưu trữ và chúng ta có thể gọi nó là bộ nhớ đệm cấp thuộc tính .

private List<User> users;
private List<User> Users
{
    get
    {
        if(users == null) users = new UserManager().GetUsers();
        return users;
    }
}

Bây giờ ở những nơi khác trong lớp, tôi sử dụng Userstài sản thay vì userstrường.

Một tình huống khả thi khác có thể là khi bạn muốn triển khai một số logic trên một trường, nhưng theo cách tập trung trong một lớp. Do đó, bạn có thể vừa tạo một GetFoo()phương thức hoặc một thuộc Footính cho footrường.

private string foo;
private string Foo
{
    get
    {
        return "Mr. " + foo;
    }
}

0

Một cái gì đó khác để xem xét:

Tính chất thực tế là một chi tiết thực hiện. Một trong những mục tiêu của OOP là cố gắng và giảm thiểu sự phơi bày của các chi tiết triển khai khi thực tế.

Lý do cho điều này là nếu bạn ẩn các thuộc tính và chỉ hiển thị chúng thông qua getters và setters, bạn có quyền kiểm soát nhiều hơn. Ví dụ, getter của bạn có thể xác thực đầu vào của nó và ngăn thuộc tính được đặt thành trạng thái không hợp lệ. Nếu khả năng thay đổi giá trị là thời gian quan trọng, thì setter cũng có thể bảo vệ chống lại điều này. Các getter, nên nhận được giá trị thực tế đắt tiền vì bất kỳ lý do gì, có thể thực hiện lười biếng khởi tạo và / hoặc bộ nhớ đệm.

Có các getters và setters tiếp xúc và các thuộc tính ẩn có nghĩa là đôi khi bạn không cần một tài sản nào cả. Trình getter của bạn có thể tính toán giá trị khi gọi hoặc ủy thác nhiệm vụ ở một nơi khác hoặc bất cứ điều gì cho phép nó đáp ứng đặc điểm kỹ thuật của nó. Điều quan trọng về lớp học của bạn là thông tin bạn có thể truy cập từ đó, chứ không phải cách thông tin đó được thể hiện trong nội bộ.

Tất nhiên, nếu không có hậu quả tiêu cực đối với bất kỳ thứ gì bên ngoài lớp có thể truy cập trực tiếp vào một tài sản, thì bạn chỉ nên công khai nó. Tuy nhiên theo kinh nghiệm của tôi, những trường hợp như vậy là tương đối ít và xa.


0

Tôi biết đây là một câu hỏi cũ và mọi thứ @Yusubov nói đều đúng, nhưng liên quan đến điểm đạn thứ hai của anh ấy về logic cụ thể trong setter, và vì tôi không thấy ai đề cập cụ thể về điều này, một ví dụ hoàn hảo sẽ là khi lớp của bạn thực hiệnINotifyPropertyChanged giao diện.

Điều này được sử dụng một tấn trong WCFSilverlight cho phép bạn biết khi nào một thuộc tính cụ thể đã thay đổi thông qua logic trong trình thiết lập của nó như trong lớp bên dưới:

public class Settings : INotifyPropertyChanged
{
    public Settings() 
    { 
        this.CountryField = String.Empty; 
    }

    private string CountryField;
    public string Country
    {
        get { return this.CountryField; }
        set
        {
            if (Object.ReferenceEquals(this.CountryField, value)) { return; }

            this.CountryField = value;
            this.RaisePropertyChanged("Country");
        } 
    }

    public event PropertyChangedEventHandler PropertyChanged;
    private void RaisePropertyChanged(string propertyName)
    {
        PropertyChangedEventHandler propertyChanged = this.PropertyChanged;

        if (propertyChanged != null)
            propertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
}
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.