Các phương pháp hay nhất về Getters, setters và thuộc tính. Java so với C #


92

Tôi đang theo học lớp C # và tôi đang cố gắng tìm ra cách tốt nhất để làm việc này. Tôi xuất thân từ nền tảng Java nên tôi chỉ quen thuộc với các phương pháp hay nhất về Java; Tôi là người mới học C #!

Trong Java nếu tôi có tài sản riêng, tôi làm điều này;

private String name;

public void setName(String name) {
   this.name = name;
}

public String getName() {
   return this.name;
}

Trong C #, tôi thấy rằng có nhiều cách để làm điều này.

Tôi có thể làm điều đó như Java:

private string name;

public void setName(string name) {
   this.name = name;
}

public string getName() {
   return this.name;
}

Hoặc tôi có thể làm theo cách này:

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

Hoặc là:

public string Name { get; set; }

Tôi nên sử dụng cái nào, và những lưu ý hoặc sự khôn khéo liên quan đến mỗi cách tiếp cận là gì? Khi tạo các lớp, tôi đang tuân theo các phương pháp hay nhất chung mà tôi biết từ Java (đặc biệt là đọc Java hiệu quả). Vì vậy, ví dụ, tôi thích tính bất biến (chỉ cung cấp bộ định tuyến khi cần thiết). Tôi chỉ tò mò muốn biết những cách thực hành này phù hợp với những cách khác nhau để cung cấp bộ định tuyến và bộ định tuyến trong C #; về cơ bản, tôi sẽ dịch các phương pháp hay nhất từ ​​thế giới Java sang C # như thế nào?

BIÊN TẬP

Tôi đã đăng điều này như một bình luận cho câu trả lời của Jon Skeet nhưng sau đó nó rất lâu:

Điều gì về một thuộc tính không tầm thường (nghĩa là có thể có quá trình xử lý và xác thực quan trọng)? Tôi vẫn có thể hiển thị nó thông qua thuộc tính công cộng nhưng với logic được gói gọn trong getset? Tại sao tôi nên / nên làm điều này vì có các phương thức setter và getter chuyên dụng (với logic xử lý và xác nhận liên quan).

Câu trả lời:


88

Pre-C # 6

Tôi sẽ sử dụng cái cuối cùng trong số này, cho một tài sản tầm thường. Lưu ý rằng tôi sẽ gọi đây là thuộc tính công cộng vì cả getters và setters đều công khai.

Tính bất biến có một chút khó khăn với các thuộc tính được triển khai tự động - bạn không thể viết một thuộc tính tự động mà chỉ có một getter; gần nhất bạn có thể đến là:

public string Foo { get; private set; }

mà không thực sự bất biến ... chỉ bất biến bên ngoài lớp học của bạn. Vì vậy, bạn có thể muốn sử dụng thuộc tính chỉ đọc thực thay thế:

private readonly string foo;
public string Foo { get { return foo; } }

Bạn chắc chắn không muốn viết getName()setName(). Trong một số trường hợp, viết các phương thức Get / Set là hợp lý hơn là sử dụng các thuộc tính, đặc biệt nếu chúng có thể đắt và bạn muốn nhấn mạnh điều đó. Tuy nhiên, bạn muốn tuân theo quy ước đặt tên .NET của PascalCase cho các phương thức và bạn sẽ không muốn một thuộc tính tầm thường như thế này được triển khai bằng các phương thức bình thường - một thuộc tính ở đây mang tính thành ngữ hơn nhiều.

C 6

Hoan hô, cuối cùng chúng ta đã có các thuộc tính chỉ đọc tự động được triển khai thích hợp:

// This can only be assigned to within the constructor
public string Foo { get; }

Tương tự như vậy đối với tài sản chỉ đọc mà làm cần phải làm một số công việc, bạn có thể sử dụng tài sản thành viên thân:

public double Area => height * width;

5
Chính xác hơn: bất kỳ đoạn mã nào reiew sẽ chỉ ra cách java là một cuộc tấn công đang bỏ qua các cấu trúc thời gian chạy của langauge AND (!) Hợp lệ và giết việc sử dụng thuộc tính dưới dạng proeprty (tức là object.property = "value"). Ở một số đội, điều này sẽ dẫn đến một cuộc nói chuyện tốt đẹp về thái độ - tùy thuộc vào thâm niên kết hợp với động cơ để sử dụng thái độ đó ở đối thủ cạnh tranh. Nghiêm túc đấy, KHÔNG CHỐNG NGÔN NGỮ. Đặc biệt là "cách" của java là một cuộc tấn công đã được chọn để không sửa đổi ngôn ngữ để hỗ trợ tài sản thực.
TomTom

1
Tôi đoán tin tốt là câu trả lời của tôi dường như không mâu thuẫn với bất cứ điều gì bạn đã đề cập. Tin xấu là ngón tay của bạn nhanh hơn của tôi rất nhiều. Cái nhìn sâu sắc và cảm ơn vì đã bổ sung chi tiết.
jeremyalan

4
Để trả lời, bạn hãy chỉnh sửa: bạn có thể sử dụng các phương thức get / set với bất kỳ logic nào trong đó mà bạn muốn. Chúng tôi thường làm điều này, đặc biệt là để xác nhận. Tuy nhiên, thực tiễn tốt nhất là không có nhiều logic chậm (ví dụ truy cập cơ sở dữ liệu), logic nguy hiểm (ném ngoại lệ) hoặc đột biến (thay đổi nhiều trạng thái) trong các thuộc tính. Một thuộc tính được mong đợi sẽ hoạt động ít nhiều giống như một trạng thái đơn giản. Thay vào đó, bất cứ điều gì nhiều hơn nên được chỉ ra bằng cách sử dụng một hàm.
CodexArcanum

2
@rtindru: Vâng, tôi biết điều đó. Hoàn toàn có thể viết thuộc tính chỉ đọc. Nhưng bạn không thể khai báo thuộc tính được triển khai tự động mà không có bộ truy cập đã đặt.
Jon Skeet


17

Nếu tất cả những gì bạn cần là một biến để lưu trữ một số dữ liệu:

public string Name { get; set; }

Muốn làm cho nó xuất hiện ở chế độ chỉ đọc?

public string Name { get; private set; }

Hoặc thậm chí tốt hơn ...

private readonly string _name;

...

public string Name { get { return _name; } }

Bạn muốn thực hiện một số kiểm tra giá trị trước khi gán thuộc tính?

public string Name 
{
   get { return m_name; }
   set
   {
      if (value == null)
         throw new ArgumentNullException("value");

      m_name = value;
   }
}

Nói chung, GetXyz () và SetXyz () chỉ được sử dụng trong một số trường hợp nhất định và bạn chỉ cần sử dụng ruột của mình khi cảm thấy phù hợp. Nói chung, tôi sẽ nói rằng tôi mong đợi hầu hết các thuộc tính get / set không chứa nhiều logic và có rất ít tác dụng phụ không mong muốn, nếu có. Nếu việc đọc một giá trị thuộc tính yêu cầu gọi một dịch vụ hoặc nhận đầu vào từ người dùng để xây dựng đối tượng mà tôi đang yêu cầu, thì tôi sẽ gói nó thành một phương thức và gọi nó là một cái gì đó giống như BuildXyz(), thay vì GetXyz().


2
Tôi sẽ không ném các ngoại lệ vào các thuộc tính, tôi muốn sử dụng các bộ thiết lập phương thức với các hợp đồng để chỉ định hành vi cụ thể như vậy. Nếu một thuộc tính thuộc loại int, tôi mong muốn mọi int đều đủ điều kiện. Theo tôi, một thứ gì đó yêu cầu ném ngoại lệ không đơn giản, theo tôi các lời gọi loại INotifyPropertyChanged nằm trong dòng đó nhiều hơn.
flndeberg

3
private setter! = immutable
piedar

piedar đã đúng. Một setter riêng chỉ có nghĩa là tôi không thể thực hiện các nhiệm vụ, nhưng tôi vẫn có thể sử dụng myList.Add(), chẳng hạn như (miễn là đối tượng có thể thay đổi, nó có thể thay đổi).

1
@flindeberg Xin lỗi nhưng tôi không đồng ý ... Còn nếu bạn có một thanh tiến trình với giá trị Min/ Max. Bạn muốn đảm bảo điều đó Max > Min? Có SetRange(Min, Max)thể có ý nghĩa nhưng sau đó làm thế nào bạn sẽ đọc lại những giá trị đó? Thuộc tính Min / Max chỉ đọc? Việc ném các ngoại lệ cho đầu vào không hợp lệ có vẻ là cách tốt nhất để xử lý điều này.
Cơ bản

12

Sử dụng thuộc tính trong C #, không phải phương thức get / set. Họ ở đó để thuận tiện cho bạn và nó là thành ngữ.

Đối với hai ví dụ C # của bạn, một ví dụ đơn giản là đường cú pháp cho cái kia. Sử dụng thuộc tính tự động nếu tất cả những gì bạn cần là một trình bao bọc đơn giản xung quanh một biến thể hiện, sử dụng phiên bản đầy đủ khi bạn cần thêm logic trong getter và / hoặc setter.


5

Trong C # ưu tiên các thuộc tính để hiển thị các trường riêng tư cho get và / hoặc set. Biểu mẫu mà bạn đề cập là một tính năng tự động thực hiện trong đó get và set tự động tạo ra một trường hỗ trợ trục xoay ẩn cho bạn.

Tôi ủng hộ các thuộc tính tự động khi có thể nhưng bạn không bao giờ nên thực hiện cặp phương thức set / get trong C #.


5
public string Name { get; set; }

Đây chỉ đơn giản là một thuộc tính được triển khai tự động và về mặt kỹ thuật giống như một thuộc tính bình thường. Một trường hỗ trợ sẽ được tạo khi biên dịch.

Tất cả các thuộc tính cuối cùng được chuyển đổi thành các hàm, do đó, việc thực hiện biên dịch thực tế cuối cùng cũng giống như bạn đã quen trong Java.

Sử dụng các thuộc tính được triển khai tự động khi bạn không phải thực hiện các thao tác cụ thể trên trường sao lưu. Sử dụng một tài sản thông thường nếu không. Sử dụng các hàm get và set khi thao tác có tác dụng phụ hoặc tốn kém về mặt tính toán, ngược lại hãy sử dụng các thuộc tính.


4

Bất kể cách nào bạn chọn trong C #, kết quả cuối cùng đều giống nhau. Bạn sẽ nhận được một biến backinng với các phương thức getter và setter riêng biệt. Bằng cách sử dụng các thuộc tính, bạn đang tuân theo các phương pháp hay nhất và do đó, bạn muốn nhận được chi tiết như thế nào.

Cá nhân tôi sẽ chọn thuộc tính tự động, phiên bản cuối cùng:, public string Name { get; set; }vì chúng chiếm ít dung lượng nhất. Và bạn luôn có thể mở rộng những thứ này trong tương lai nếu bạn cần thêm thứ gì đó như xác thực.


4

Bất cứ khi nào có thể, tôi thích công khai hơn string Name { get; set; }vì nó ngắn gọn và dễ đọc. Tuy nhiên, đôi khi điều này là cần thiết

private string name;

public string Name {
   get { return name; }
   set { name = value; }
}

2
bạn có thể giải thích khi nào và tại sao loại điều đó là cần thiết?
Jesper

Ngay bây giờ tôi không thể nghĩ ra lý do để sử dụng phiên bản thứ 2. Tôi chỉ nói 'có thể là những lúc cần thiết'. Tôi ghét sử dụng sự tuyệt đối trừ khi tôi tích cực.
SquidScareMe

3
Và tại sao lại là 'lolz'? Bạn có thể thể hiện sự ủng hộ của mình cho một nhận xét bằng cách ủng hộ.
SquidScareMe

1
Một lý do để sử dụng một trường ủng hộ rõ ràng là khi bạn muốn làm thread-safe hoạt động nguyên tử / chống lại các giá trị
cơ bản

4

Trong C #, cách ưu tiên là thông qua các thuộc tính hơn là getX()setX()các phương thức. Ngoài ra, lưu ý rằng C # không yêu cầu các thuộc tính phải có cả get và set - bạn có thể có thuộc tính get-only và set-only.

public boolean MyProperty
{
    get { return something; }
}

public boolean MyProperty
{
    set { this.something = value; }
}

4

Trước tiên, hãy để tôi giải thích những gì bạn đã viết:

// private member -- not a property
private string name;

/// public method -- not a property
public void setName(string name) {
   this.name = name;
}

/// public method -- not a property
public string getName() {
   return this.name;
}

// yes it is property structure before .Net 3.0
private string name;
public string Name {
   get { return name; }
   set { name = value; }
}

Cấu trúc này ngày nay cũng được sử dụng nhưng nó phù hợp nhất nếu bạn muốn thực hiện một số chức năng bổ sung, chẳng hạn như khi một giá trị được đặt, bạn có thể phân tích cú pháp để viết hoa nó và lưu nó trong thành viên riêng tư để thay đổi sử dụng nội bộ.

Với .net framework 3.0

// this style is introduced, which is more common, and suppose to be best
public string Name { get; set; }

//You can more customize it
public string Name
{
    get;
    private set;    // means value could be set internally, and accessed through out
}

Chúc bạn may mắn hơn trong C #


3

Như đã đề cập, tất cả các cách tiếp cận này đều dẫn đến cùng một kết quả. Điều quan trọng nhất là bạn chọn một quy ước và gắn bó với nó. Tôi thích sử dụng hai ví dụ thuộc tính cuối cùng.


2

giống như hầu hết các câu trả lời ở đây, hãy sử dụng thuộc tính Tự động. Trực quan, ít dòng mã hơn và rõ ràng hơn. Nếu bạn nên tuần tự hóa lớp của mình, hãy đánh dấu lớp [Serializable]/ với [DataConract]thuộc tính. Và nếu bạn đang sử dụng, hãy [DataContract]đánh dấu thành viên với

[DataMember(Name="aMoreFriendlyName")]
public string Name { get; set; }

Người thiết lập tư nhân hoặc công cộng tùy thuộc vào sở thích của bạn.

Cũng lưu ý rằng các thuộc tính tự động yêu cầu cả getters và setters (công khai hoặc riêng tư).

/*this is invalid*/
public string Name 
{ 
    get; 
   /* setter omitted to prove the point*/
}

Ngoài ra, nếu bạn chỉ muốn get / set, hãy tự tạo trường hỗ trợ


0

Tôi nên sử dụng cái nào, và những lưu ý hoặc sự khôn khéo liên quan đến mỗi cách tiếp cận là gì?

Khi đi với các thuộc tính, có một điều lưu ý chưa được đề cập đến: Với các thuộc tính, bạn không thể có bất kỳ tham số nào của getters hoặc setters của mình.

Ví dụ, hãy tưởng tượng bạn muốn truy xuất một danh sách các mục và cũng muốn áp dụng một bộ lọc cùng một lúc. Với phương thức get, bạn có thể viết những thứ như sau:

obj.getItems(filter);

Ngược lại, với một tài sản, trước tiên bạn buộc phải trả lại tất cả các mặt hàng

obj.items

và sau đó áp dụng bộ lọc trong bước tiếp theo hoặc bạn phải thêm các thuộc tính chuyên dụng để hiển thị các mục được lọc theo các tiêu chí khác nhau, điều này sẽ sớm làm phình ra API của bạn:

obj.itemsFilteredByX
obj.itemsFilteredByY

Điều đôi khi có thể gây phiền toái là khi bạn bắt đầu với một thuộc tính, chẳng hạn như obj.itemssau đó phát hiện ra rằng tham số getter- hoặc setter-parametrization là cần thiết hoặc sẽ giúp mọi thứ dễ dàng hơn cho người dùng class-API. Bây giờ bạn sẽ cần phải viết lại API của mình và sửa đổi tất cả những vị trí đó trong mã truy cập thuộc tính này của bạn hoặc tìm một giải pháp thay thế. Ngược lại, với một phương thức get, chẳng hạn obj.getItems(), bạn có thể chỉ cần mở rộng chữ ký phương thức của mình để chấp nhận một đối tượng "cấu hình" tùy chọn, ví dụ:obj.getItems(options) mà không cần phải viết lại tất cả những nơi gọi phương thức của bạn.

Điều đó đang được nói, các thuộc tính (tự động triển khai) trong C # vẫn là các phím tắt rất hữu ích (vì các lý do khác nhau được đề cập ở đây) vì hầu hết thời gian có thể không cần tham số hóa - nhưng điều này là đúng.

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.