Sự khác biệt giữa một lĩnh vực và một tài sản là gì?


1130

Trong C #, điều gì làm cho một trường khác với một thuộc tính và khi nào một trường nên được sử dụng thay vì một thuộc tính?


32
Microsoft trực tiếp trả lời câu hỏi này (cho tất cả các ngôn ngữ .NET) như một phần của Nguyên tắc thiết kế thành viên . Để biết chi tiết, xem các bài viết Thiết kế tài sảnThiết kế hiện trường . Lưu ý có sự phân biệt giữa dụ thành viên và tĩnh thành viên.
DavidRR

Câu trả lời:


979

Các thuộc tính phơi bày các trường. Các trường nên (hầu như luôn luôn) được giữ riêng tư cho một lớp và được truy cập thông qua các thuộc tính get và set. Các thuộc tính cung cấp một mức độ trừu tượng cho phép bạn thay đổi các trường trong khi không ảnh hưởng đến cách bên ngoài mà chúng được truy cập bởi những thứ sử dụng lớp của bạn.

public class MyClass
{
    // this is a field.  It is private to your class and stores the actual data.
    private string _myField;

    // this is a property. When accessed it uses the underlying field,
    // but only exposes the contract, which will not be affected by the underlying field
    public string MyProperty
    {
        get
        {
            return _myField;
        }
        set
        {
            _myField = value;
        }
    }

    // This is an AutoProperty (C# 3.0 and higher) - which is a shorthand syntax
    // used to generate a private field for you
    public int AnotherProperty{get;set;} 
}

@Kent chỉ ra rằng Thuộc tính không bắt buộc phải đóng gói các trường, chúng có thể thực hiện phép tính trên các trường khác hoặc phục vụ các mục đích khác.

@GSS chỉ ra rằng bạn cũng có thể thực hiện logic khác, chẳng hạn như xác thực, khi một thuộc tính được truy cập, một tính năng hữu ích khác.


185
Cần lưu ý rằng các thuộc tính không bắt buộc phải đóng gói các trường. Không thể có lĩnh vực nào phía sau tài sản. Nó có thể là một phép tính hoặc trả về một hằng số hoặc bất cứ điều gì.
Kent Boogaart

9
"trong khi không ảnh hưởng đến cách bên ngoài mà chúng được truy cập bởi những thứ sử dụng lớp của bạn." Hãy tha thứ cho tôi nếu tôi hiểu sai, nhưng tại sao cần phải sửa đổi truy cập trước các thuộc tính, nếu trường phía sau nó dường như xử lý việc này? tức là tại sao làm cho một tài sản bất cứ điều gì khác ngoài công cộng?
Chucky

18
Câu trả lời của bạn là đúng trước khi chỉnh sửa và nhận xét không chính xác. Một thuộc tính phải luôn đóng gói một hoặc nhiều trường và không bao giờ thực hiện bất kỳ thao tác nâng hoặc xác nhận nặng nào. Nếu bạn cần một thuộc tính như Tên người dùng hoặc Mật khẩu để xác thực, hãy thay đổi loại của chúng từ chuỗi thành Đối tượng giá trị . Có một hợp đồng bất thành văn giữa một người tạo ra lớp và người tiêu dùng. Các trường giữ trạng thái, Thuộc tính hiển thị trạng thái bằng cách sử dụng một hoặc nhiều trường, Trạng thái thay đổi Voids (nâng nặng) và Hàm thực hiện các truy vấn (nâng nặng). Đây không phải là đá, chỉ là những kỳ vọng lỏng lẻo.
Suamere 6/11/2015

6
@jpaugh Nếu tôi là người tiêu dùng đẳng cấp, tôi tuân theo các hợp đồng do người tạo lớp đặt ra. Nếu một tài sản là string, hợp đồng của tôi là: chỉ định bất kỳ ký tự nào dài tới ~ 2 tỷ. Nếu một tài sản là DateTime, hợp đồng của tôi là: gán bất kỳ số nào trong giới hạn của DateTime, mà tôi có thể tra cứu. Nếu người tạo thêm các ràng buộc cho setters, các ràng buộc đó không được truyền đạt. Nhưng nếu, thay vào đó, người tạo thay đổi loại từ stringthành Surname, thì lớp Họ mới của họ truyền đạt các ràng buộc và thuộc tính public Surname LastNamekhông có xác thực setter. Ngoài ra, Surnamelà tái sử dụng.
Suamere

4
Và vì Surname, trong ví dụ của tôi, có thể tái sử dụng, bạn không cần phải lo lắng về việc sao chép / dán các xác nhận đó trong một bộ thuộc tính đến các vị trí khác trong mã. Cũng không tự hỏi liệu việc xác thực Họ có ở nhiều nơi hay không nếu bạn thay đổi quy tắc kinh doanh cho Tên họ. Kiểm tra liên kết tôi đã đăng về Đối tượng giá trị
Suamere

261

Các nguyên tắc lập trình hướng đối tượng nói rằng, hoạt động bên trong của một lớp nên được ẩn khỏi thế giới bên ngoài. Nếu bạn trưng ra một lĩnh vực mà bạn thực chất sẽ phơi bày việc triển khai nội bộ của lớp. Do đó, chúng tôi bao bọc các trường bằng Thuộc tính (hoặc phương thức trong trường hợp của Java) để cung cấp cho chúng tôi khả năng thay đổi việc triển khai mà không vi phạm mã tùy thuộc vào chúng tôi. Xem như chúng ta có thể đặt logic trong Thuộc tính cũng cho phép chúng ta thực hiện logic xác thực, v.v. nếu chúng ta cần nó. C # 3 có khái niệm khó hiểu về tự động. Điều này cho phép chúng tôi chỉ cần xác định Thuộc tính và trình biên dịch C # 3 sẽ tạo trường riêng cho chúng tôi.

public class Person
{
   private string _name;

   public string Name
   {
      get
      {
         return _name;
      }
      set
      {
         _name = value;
      }
   }
   public int Age{get;set;} //AutoProperty generates private field for us
}

89
+1 để đề cập đến tự động hóa - Tôi nghĩ rằng đây là điều mà nhiều câu trả lời ở đây (và các nơi khác) đã quên đưa vào. Nếu không có lời giải thích này, vẫn có thể khá khó để nắm bắt những gì public int myVar { get; set; }thực sự đại diện cho (và tôi cho rằng đó là lý do cho ít nhất 50% số lượt truy cập mà câu hỏi này nhận được).
Priidu Neemre

7
+1 cũng để đề cập đến tự động và đề cập đến cách thức hoạt động của nó ("AutoProperty tạo trường riêng cho chúng tôi") Đây là câu trả lời tôi đang tìm kiếm cho một câu hỏi tôi có. Khi nghiên cứu tôi không thấy trên trang của MSDN về họ bất kỳ dấu hiệu nào cho thấy một trường riêng được tạo và đang gây nhầm lẫn. Tôi đoán đó là những gì có nghĩa là? "Các thuộc tính được phép trên các thuộc tính được triển khai tự động nhưng rõ ràng không phải trên các trường sao lưu vì chúng không thể truy cập được từ mã nguồn của bạn. Nếu bạn phải sử dụng một thuộc tính trên trường sao lưu của thuộc tính, chỉ cần tạo thuộc tính thông thường." nhưng không chắc chắn
Nyra

3
Lưu ý rằng ví dụ đã cho không đóng gói squat. Thuộc tính này cung cấp quyền truy cập đầy đủ 100% vào trường riêng, vì vậy đây hoàn toàn không hướng đối tượng. Bạn cũng có thể có một lĩnh vực công cộng trong trường hợp này. Được cấp, nó giúp (bên lề) mã hóa cấu trúc lại trong tương lai, nhưng bất kỳ IDE nào có giá trị, nó có thể chuyển đổi một trường thành một tài sản với một vài lần nhấn phím. Câu trả lời có thể đúng về mặt kỹ thuật về cách thức hoạt động của các thuộc tính, nhưng nó không đưa ra một "lời giải thích OOP" tốt cho việc sử dụng chúng.
sara

2
@kai Tôi đồng ý rằng câu trả lời đơn giản hóa mọi thứ và không thể hiện tất cả sức mạnh của một tài sản tự động, tuy nhiên tôi không đồng ý rằng đây không phải là hướng đối tượng. Bạn có thể muốn kiểm tra sự khác biệt giữa các trường và thuộc tính . Các trường không thể là ảo và virtualchính nó là một phần của lập trình hướng đối tượng.
Gobe

Cấp, có một số khác biệt chức năng. Tôi sẽ không gọi virtualOOP mỗi se mặc dù. Đây là một công cụ cho phép đa hình, là một trong những công cụ quan trọng mà ENABLES OOP. Mặc dù vậy, bản thân nó không phải là OOP và không có gì là OOP về tự động công khai. Tôi sẽ không tính những thứ như phản chiếu hoặc cơ sở dữ liệu liên quan đến OOP. Thông thường tôi sẽ không quá khoa trương về điều đó, nhưng câu trả lời đã đề cập cụ thể đến các nguyên tắc OO là động lực đằng sau ví dụ về mã và tôi không đồng ý với điều đó.
sara

164

Một sự khác biệt quan trọng là các giao diện có thể có các thuộc tính nhưng không có các trường. Điều này, với tôi, nhấn mạnh rằng các thuộc tính nên được sử dụng để xác định giao diện chung của một lớp trong khi các trường được sử dụng trong các hoạt động riêng tư, nội bộ của một lớp. Theo quy định, tôi hiếm khi tạo các trường công khai và tương tự, tôi hiếm khi tạo các thuộc tính không công khai.


98

Tôi sẽ cung cấp cho bạn một vài ví dụ về việc sử dụng các thuộc tính có thể khiến bánh răng quay:

  • Khởi tạo lười biếng : Nếu bạn có một thuộc tính của một đối tượng đắt tiền để tải, nhưng không truy cập được nhiều như vậy trong các lần chạy mã thông thường, bạn có thể trì hoãn việc tải nó qua tài sản. Theo cách đó, nó chỉ ngồi ở đó, nhưng lần đầu tiên một mô-đun khác cố gắng gọi thuộc tính đó, nó sẽ kiểm tra xem trường bên dưới có phải không - nếu có, nó sẽ tiếp tục và tải nó, không biết đến mô-đun gọi. Điều này rất có thể tăng tốc độ khởi tạo đối tượng.
  • Theo dõi bẩn: Điều mà tôi thực sự đã học được từ câu hỏi của chính mình ở đây trên StackOverflow. Khi tôi có nhiều đối tượng mà các giá trị có thể đã thay đổi trong quá trình chạy, tôi có thể sử dụng thuộc tính để theo dõi xem chúng có cần được lưu lại vào cơ sở dữ liệu hay không. Nếu không phải một thuộc tính duy nhất của một đối tượng đã thay đổi, cờ IsDenty sẽ không bị vấp, và do đó chức năng lưu sẽ bỏ qua khi quyết định những gì cần quay lại cơ sở dữ liệu.

1
Một câu hỏi về theo dõi bẩn: nếu tôi có thể thay đổi trường trực tiếp thì tôi không biết điều đó có thể được thực hiện hay không, tôi có thể nói: "đối tượng không cần phải lưu nếu không một LELDNH VỰC của một đối tượng đã thay đổi" do đó theo dõi bẩn sẽ không phải là một sự khác biệt, tôi có thiếu điều gì không?
trang web

2
@juanpastas: Ưu điểm của các thuộc tính liên quan đến theo dõi bẩn là nếu setters thuộc tính sẽ đặt cờ "bẩn", thì trong trường hợp cờ không đặt mã sẽ không phải kiểm tra các giá trị của bất kỳ thuộc tính nào để xem nếu họ có thể đã thay đổi. Ngược lại, nếu một đối tượng phơi bày các thuộc tính của nó dưới dạng các trường, thì nội dung của tất cả các trường phải được so sánh với giá trị trước đó (không chỉ thêm thời gian để thực hiện so sánh, mà còn có nghĩa là mã phải giá trị trước đó).
supercat

Đó là những cái tốt Nó cũng cho phép bạn kích hoạt các phương thức (dưới dạng sự kiện) hoặc ghi nhật ký khi giá trị được đặt hoặc đọc.
coloboxp

54

Sử dụng Thuộc tính, bạn có thể nâng cao một sự kiện, khi giá trị của thuộc tính được thay đổi (còn gọi là PropertyChangedEvent) hoặc trước khi giá trị được thay đổi để hỗ trợ hủy bỏ.

Điều này là không thể với các trường (truy cập trực tiếp vào).

public class Person {
 private string _name;

 public event EventHandler NameChanging;     
 public event EventHandler NameChanged;

 public string Name{
  get
  {
     return _name;
  }
  set
  {
     OnNameChanging();
     _name = value;
     OnNameChanged();
  }
 }

 private void OnNameChanging(){       
     NameChanging?.Invoke(this,EventArgs.Empty);       
 }

 private void OnNameChanged(){
     NameChanged?.Invoke(this,EventArgs.Empty);
 }
}

3
Tôi mất một thời gian dài để tìm thấy điều này. Đây là một MVVM . Cảm ơn bạn ! :)

46

Vì nhiều người trong số họ đã giải thích với những ưu và nhược điểm kỹ thuật PropertiesField, đã đến lúc đi vào các ví dụ thời gian thực.

1. Thuộc tính cho phép bạn đặt mức truy cập chỉ đọc

Hãy xem xét trường hợp dataTable.Rows.CountdataTable.Columns[i].Caption. Họ đến từ lớp học DataTablevà cả hai đều công khai với chúng tôi. Sự khác biệt ở cấp độ truy cập đối với chúng là chúng ta không thể đặt giá trị thành dataTable.Rows.Countnhưng chúng ta có thể đọc và ghi vào dataTable.Columns[i].Caption. Điều đó có thể thông qua Field? Không!!! Điều này có thể được thực hiện với Propertieschỉ.

public class DataTable
{
    public class Rows
    {       
       private string _count;        

       // This Count will be accessable to us but have used only "get" ie, readonly
       public int Count
       {
           get
           {
              return _count;
           }       
       }
    } 

    public class Columns
    {
        private string _caption;        

        // Used both "get" and "set" ie, readable and writable
        public string Caption
        {
           get
           {
              return _caption;
           }
           set
           {
              _caption = value;
           }
       }       
    } 
}

2. Thuộc tính trong PropertyGrid

Bạn có thể đã làm việc với ButtonVisual Studio. Các thuộc tính của nó được hiển thị PropertyGridgiống như Text, Namev.v. Khi chúng ta kéo và thả nút và khi chúng ta nhấp vào thuộc tính, nó sẽ tự động tìm lớp Buttonvà bộ lọc Propertiesvà hiển thị trong đó PropertyGrid(nơi PropertyGridsẽ không hiển thị Fieldngay cả khi chúng ở chế độ công khai).

public class Button
{
    private string _text;        
    private string _name;
    private string _someProperty;

    public string Text
    {
        get
        {
           return _text;
        }
        set
        {
           _text = value;
        }
   } 

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

   [Browsable(false)]
   public string SomeProperty
   {
        get
        {
           return _someProperty;
        }
        set
        {
           _someProperty= value;
        }
   } 

Trong PropertyGrid, các thuộc tính NameTextsẽ được hiển thị, nhưng không SomeProperty. Tại sao??? Bởi vì Thuộc tính có thể chấp nhận Thuộc tính . Nó không hiển thị trong trường hợp [Browsable(false)]sai.

3. Có thể thực thi các câu lệnh bên trong Thuộc tính

public class Rows
{       
    private string _count;        


    public int Count
    {
        get
        {
           return CalculateNoOfRows();
        }  
    } 

    public int CalculateNoOfRows()
    {
         // Calculation here and finally set the value to _count
         return _count;
    }
}

4. Chỉ các thuộc tính có thể được sử dụng trong Binding Source

Binding Source giúp chúng tôi giảm số lượng dòng mã. Fieldskhông được chấp nhận bởi BindingSource. Chúng ta nên sử dụng Propertiescho điều đó.

5. Chế độ gỡ lỗi

Hãy xem xét chúng tôi đang sử dụng Fieldđể giữ một giá trị. Tại một số điểm chúng ta cần gỡ lỗi và kiểm tra xem giá trị đang trở thành null cho trường đó. Sẽ rất khó để thực hiện khi số lượng dòng mã là hơn 1000. Trong các tình huống như vậy, chúng ta có thể sử dụng Propertyvà có thể đặt chế độ gỡ lỗi bên trong Property.

   public string Name
   {
        // Can set debug mode inside get or set
        get
        {
           return _name;
        }
        set
        {
           _name = value;
        }
   }

Đây là những sự thật thú vị, nhưng bạn đang thiếu quan điểm về triết lý thuộc tính và lĩnh vực.
David Ferenczy Rogožan

PHILISOPHY có ý nghĩa gì? @Dawid Ferenczy
Sarath Avanavu

Xem ví dụ đánh dấu câu trả lời. Nhưng bạn nhận thấy rằng bạn chỉ cung cấp một ví dụ sử dụng, vì sự khác biệt giữa các trường và thuộc tính đã được mô tả, vì vậy hãy quên nhận xét của tôi, xin vui lòng :)
David Ferenczy Rogožan

2
Đọc câu đầu tiên của tôi trong câu trả lời của tôi. Tôi đặc biệt nói rằng tôi sẽ không lặp lại mọi thứ ở đây. Điều đó thật vô nghĩa !!! Mọi người trước tiên sẽ xem mô tả trước, sau đó là các ví dụ. Câu trả lời được đánh dấu cho mô tả tốt, nhưng tôi đã thêm vào một số tình huống và ví dụ thời gian thực có ý nghĩa. Vui lòng đảm bảo rằng bạn nghĩ theo quan điểm của độc giả trước khi bình luận @Dawid Ferenczy
Sarath Avanavu 04/07/2015

1
Tôi đã đọc nó, nhưng bạn đã không đọc bình luận trước đây của tôi rõ ràng: " Nhưng bạn nhận thấy rằng bạn chỉ đang cung cấp một ví dụ sử dụng, vì sự khác biệt giữa các trường và thuộc tính đã được mô tả, vì vậy hãy quên nhận xét của tôi, vui lòng :) " .
David Ferenczy Rogožan

32

KHÁC BIỆT - SỬ DỤNG (khi nào và tại sao)

Một trường là một biến được khai báo trực tiếp trong một lớp hoặc struct. Một lớp hoặc struct có thể có các trường đối tượng hoặc trường tĩnh hoặc cả hai. Nói chung, bạn chỉ nên sử dụng các trường cho các biến có khả năng truy cập riêng tư hoặc được bảo vệ . Dữ liệu mà lớp của bạn hiển thị với mã máy khách nên được cung cấp thông qua các phương thức, thuộc tính và bộ chỉ mục. Bằng cách sử dụng các cấu trúc này để truy cập gián tiếp vào các trường bên trong, bạn có thể bảo vệ chống lại các giá trị đầu vào không hợp lệ.

Một tài sản là một thành viên cung cấp một cơ chế linh hoạt để đọc, viết hoặc tính giá trị của một trường riêng. Thuộc tính có thể được sử dụng như thể chúng là dữ liệu công cộng thành viên, nhưng họ thực sự là phương pháp đặc biệt gọi là bộ truy xuất . Điều này cho phép dữ liệu được truy cập dễ dàng và vẫn giúp thúc đẩy sự an toàn và linh hoạt của các phương pháp . Các thuộc tính cho phép một lớp thể hiện một cách công khai để nhận và thiết lập các giá trị, trong khi ẩn mã triển khai hoặc xác minh. Một bộ truy cập thuộc tính get được sử dụng để trả về giá trị thuộc tính và một bộ truy cập được thiết lập được sử dụng để gán một giá trị mới.


Đây là một câu trả lời tuyệt vời, thực sự đã giúp tôi hiểu điều này.
Steve Bauman

14

Các thuộc tính có ưu điểm chính là cho phép bạn thay đổi cách truy cập dữ liệu trên một đối tượng mà không phá vỡ giao diện công khai. Ví dụ: nếu bạn cần thêm xác thực bổ sung hoặc để thay đổi trường được lưu trữ thành tính toán, bạn có thể thực hiện dễ dàng nếu ban đầu bạn hiển thị trường dưới dạng một thuộc tính. Nếu bạn chỉ tiếp xúc một trường trực tiếp, thì bạn sẽ phải thay đổi giao diện chung của lớp để thêm chức năng mới. Thay đổi đó sẽ phá vỡ các máy khách hiện tại, yêu cầu chúng được biên dịch lại trước khi chúng có thể sử dụng phiên bản mã mới của bạn.

Nếu bạn viết một thư viện lớp được thiết kế để tiêu thụ rộng rãi (như .NET Framework, được sử dụng bởi hàng triệu người), đó có thể là một vấn đề. Tuy nhiên, nếu bạn đang viết một lớp được sử dụng nội bộ bên trong một cơ sở mã nhỏ (giả sử <= 50 K dòng), thì đó thực sự không phải là vấn đề lớn, bởi vì không ai sẽ bị ảnh hưởng xấu bởi những thay đổi của bạn. Trong trường hợp đó, nó thực sự chỉ đến sở thích cá nhân.


11

Các thuộc tính hỗ trợ truy cập không đối xứng, tức là bạn có thể có một getter và setter hoặc chỉ một trong hai. Các thuộc tính tương tự hỗ trợ khả năng truy cập riêng cho getter / setter. Các trường luôn đối xứng, tức là bạn luôn có thể nhận và đặt giá trị. Ngoại lệ cho điều này là các trường chỉ đọc mà rõ ràng không thể được đặt sau khi khởi tạo.

Các thuộc tính có thể chạy trong một thời gian rất dài, có tác dụng phụ và thậm chí có thể ném ngoại lệ. Các lĩnh vực là nhanh chóng, không có tác dụng phụ, và sẽ không bao giờ ném ngoại lệ. Do tác dụng phụ, một thuộc tính có thể trả về một giá trị khác nhau cho mỗi cuộc gọi (như trường hợp của DateTime.Now, tức là DateTime.Now không phải lúc nào cũng bằng DateTime.Now). Các trường luôn trả về cùng một giá trị.

Các trường có thể được sử dụng cho các tham số out / ref, thuộc tính có thể không. Các thuộc tính hỗ trợ logic bổ sung - điều này có thể được sử dụng để thực hiện tải lười biếng trong số những thứ khác.

Các thuộc tính hỗ trợ mức độ trừu tượng bằng cách đóng gói bất cứ thứ gì có nghĩa là lấy / đặt giá trị.

Sử dụng các thuộc tính trong hầu hết / tất cả các trường hợp, nhưng cố gắng tránh tác dụng phụ.


Các trường có thể có tất cả các vấn đề về chi phí của các thuộc tính khi kiểu dữ liệu của trường là một đối tượng có quá tải toán tử chuyển đổi - đó là một hình ảnh tinh tế.
Andy Dent

1
Thuộc tính không bao giờ nên có tác dụng phụ. Ngay cả trình gỡ lỗi giả định nó có thể đánh giá chúng một cách an toàn.
Craig Gidney

@Strilanc: Tôi hoàn toàn đồng ý, tuy nhiên, điều đó không phải lúc nào cũng đúng. Đối với trình gỡ lỗi, có nhiều vấn đề với FuncEval nếu đó là những gì bạn đang nói.
Brian Rasmussen

11

Trong nền một thuộc tính được biên dịch thành các phương thức. Vì vậy, một Nametài sản được biên dịch vào get_Name()set_Name(string value). Bạn có thể thấy điều này nếu bạn nghiên cứu mã được biên dịch. Vì vậy, có một (rất) hiệu suất nhỏ khi sử dụng chúng. Thông thường, bạn sẽ luôn sử dụng Thuộc tính nếu bạn để một trường ra bên ngoài và bạn sẽ thường sử dụng nó trong nội bộ nếu bạn cần xác thực giá trị.


7

Khi bạn muốn biến riêng (trường) của bạn có thể truy cập được vào đối tượng của lớp từ các lớp khác, bạn cần tạo thuộc tính cho các biến đó.

ví dụ: nếu tôi có các biến có tên là "id" và "name" là riêng tư nhưng có thể xảy ra trường hợp biến này cần cho hoạt động đọc / ghi bên ngoài lớp. Trong tình huống đó, thuộc tính có thể giúp tôi lấy biến đó để đọc / ghi tùy thuộc vào get / set được xác định cho thuộc tính. Một thuộc tính có thể là readonly / writeonly / readwrite cả hai.

đây là bản demo

class Employee
{
    // Private Fields for Employee
    private int id;
    private string name;

    //Property for id variable/field
    public int EmployeeId
    {
       get
       {
          return id;
       }
       set
       {
          id = value;
       }
    }

    //Property for name variable/field
    public string EmployeeName
    {
       get
       {
          return name;
       }
       set
       {
          name = value;
       }
   }
}

class MyMain
{
    public static void Main(string [] args)
    {
       Employee aEmployee = new Employee();
       aEmployee.EmployeeId = 101;
       aEmployee.EmployeeName = "Sundaran S";
    }
}

6

Câu hỏi thứ hai ở đây, "khi nào nên sử dụng một lĩnh vực thay vì một tài sản?", Chỉ được chạm vào một thời gian ngắn trong câu trả lời khác nàyloại này cũng vậy , nhưng không thực sự nhiều chi tiết.

Nói chung, tất cả các câu trả lời khác là tại chỗ về thiết kế tốt: thích phơi bày các thuộc tính hơn phơi bày các trường. Trong khi bạn có thể sẽ không thường xuyên thấy mình nói "wow, hãy tưởng tượng bao nhiêu điều tồi tệ hơn là nếu tôi đã thực hiện điều này một lĩnh vực thay vì một tài sản", đó là rất nhiều hiếm hơn để nghĩ về một tình huống mà bạn có thể nói "wow, cảm ơn Chúa tôi đã sử dụng một lĩnh vực ở đây thay vì một tài sản. "

Nhưng có một lợi thế là các trường có các thuộc tính và đó là khả năng của chúng được sử dụng làm tham số "ref" / "out". Giả sử bạn có một phương thức với chữ ký sau:

public void TransformPoint(ref double x, ref double y);

và giả sử rằng bạn muốn sử dụng phương thức đó để chuyển đổi một mảng được tạo như thế này:

System.Windows.Point[] points = new Point[1000000];
Initialize(points);

Tôi nghĩ đây là cách nhanh nhất để làm điều đó, vì XY là các thuộc tính:

for (int i = 0; i < points.Length; i++)
{
    double x = points[i].X;
    double y = points[i].Y;
    TransformPoint(ref x, ref y);
    points[i].X = x;
    points[i].Y = y;
}

Và điều đó sẽ khá tốt! Trừ khi bạn có số đo chứng minh khác, không có lý do gì để ném mùi hôi thối. Nhưng tôi tin rằng nó không được đảm bảo về mặt kỹ thuật để nhanh như thế này:

internal struct MyPoint
{
    internal double X;
    internal double Y;
}

// ...

MyPoint[] points = new MyPoint[1000000];
Initialize(points);

// ...

for (int i = 0; i < points.Length; i++)
{
    TransformPoint(ref points[i].X, ref points[i].Y);
}

Tự mình thực hiện một số phép đo , phiên bản với các trường chiếm khoảng 61% thời gian là phiên bản có thuộc tính (.NET 4.6, Windows 7, x64, chế độ phát hành, không có trình gỡ lỗi kèm theo). TransformPointPhương pháp càng đắt tiền , sự khác biệt càng trở nên rõ rệt. Để tự lặp lại điều này, hãy chạy với dòng đầu tiên nhận xét và với nó không nhận xét.

Ngay cả khi không có lợi ích hiệu suất cho các mục trên, vẫn có những nơi khác có thể sử dụng các tham số ref và out có thể có ích, chẳng hạn như khi gọi các phương thức Interlocked hoặc Volility . Lưu ý: Trong trường hợp điều này là mới đối với bạn, Volility về cơ bản là một cách để có được cùng một hành vi được cung cấp bởi volatiletừ khóa. Như vậy, như vậy volatile, nó không giải quyết một cách kỳ diệu tất cả các tai ương an toàn luồng như tên của nó cho thấy rằng nó có thể.

Tôi chắc chắn không muốn có vẻ như tôi ủng hộ rằng bạn đi "oh, tôi nên bắt đầu phơi bày các trường thay vì tài sản." Vấn đề là nếu bạn cần thường xuyên sử dụng các thành viên này trong các cuộc gọi có tham số "ref" hoặc "out", đặc biệt là một loại giá trị đơn giản không thể cần bất kỳ yếu tố giá trị gia tăng nào của các thuộc tính, một cuộc tranh cãi có thể được đưa ra.


6

Mặc dù các trường và thuộc tính trông giống nhau, nhưng chúng là 2 yếu tố ngôn ngữ hoàn toàn khác nhau.

  1. Các trường là cơ chế duy nhất làm thế nào để lưu trữ dữ liệu ở cấp độ lớp. Các trường là các biến khái niệm ở phạm vi lớp. Nếu bạn muốn lưu trữ một số dữ liệu cho các thể hiện của các lớp (đối tượng) của bạn, bạn cần sử dụng các trường. Không còn lựa chọn nào khác. Các thuộc tính không thể lưu trữ bất kỳ dữ liệu nào, mặc dù, có vẻ như chúng có thể làm như vậy. Nhìn phía dưới.

  2. Mặt khác tài sản không bao giờ lưu trữ dữ liệu. Chúng chỉ là các cặp phương thức (lấy và đặt) có thể được gọi một cách tổng hợp theo cách tương tự như các trường và trong hầu hết các trường hợp chúng truy cập (để đọc hoặc ghi) các trường, đó là nguồn gốc của một số nhầm lẫn. Nhưng vì các phương thức thuộc tính là (với một số hạn chế như nguyên mẫu cố định) nên các phương thức C # thông thường, chúng có thể làm bất cứ phương thức thông thường nào có thể làm. Nó có nghĩa là họ có thể có 1000 dòng mã, họ có thể ném ngoại lệ, gọi các phương thức khác, thậm chí có thể là ảo, trừu tượng hoặc ghi đè. Điều làm cho các thuộc tính trở nên đặc biệt, là thực tế là trình biên dịch C # lưu trữ một số siêu dữ liệu bổ sung vào các cụm có thể được sử dụng để tìm kiếm các thuộc tính cụ thể - tính năng được sử dụng rộng rãi.

Nhận và thiết lập các phương thức thuộc tính có các nguyên mẫu sau.

PROPERTY_TYPE get();

void set(PROPERTY_TYPE value);

Vì vậy, điều đó có nghĩa là các thuộc tính có thể được 'mô phỏng' bằng cách xác định một trường và 2 phương thức tương ứng.

class PropertyEmulation
{
    private string MSomeValue;

    public string GetSomeValue()
    {
        return(MSomeValue);
    }

    public void SetSomeValue(string value)
    {
        MSomeValue=value;
    }
}

Việc mô phỏng thuộc tính như vậy là điển hình cho các ngôn ngữ lập trình không hỗ trợ các thuộc tính - như C ++ tiêu chuẩn. Trong C #, bạn phải luôn thích các thuộc tính như cách truy cập vào các trường của mình.

Bởi vì chỉ các trường có thể lưu trữ dữ liệu, điều đó có nghĩa là nhiều lớp trường chứa hơn, nhiều đối tượng bộ nhớ của lớp đó sẽ tiêu thụ. Mặt khác, việc thêm các thuộc tính mới vào một lớp không làm cho các đối tượng của lớp đó lớn hơn. Dưới đây là ví dụ.

class OneHundredFields
{
        public int Field1;
        public int Field2;
        ...
        public int Field100;
}

OneHundredFields Instance=new OneHundredFields() // Variable 'Instance' consumes 100*sizeof(int) bytes of memory.

class OneHundredProperties
{
    public int Property1
    {
        get
        {
            return(1000);
        }
        set
        {
            // Empty.
        }
    }

    public int Property2
    {
        get
        {
            return(1000);
        }
        set
        {
            // Empty.
        }
    }

    ...

    public int Property100
    {
        get
        {
            return(1000);
        }
        set
        {
            // Empty.
        }
    }
}

OneHundredProperties Instance=new OneHundredProperties() // !!!!! Variable 'Instance' consumes 0 bytes of memory. (In fact a some bytes are consumed becasue every object contais some auxiliarity data, but size doesn't depend on number of properties).

Mặc dù các phương thức thuộc tính có thể làm bất cứ điều gì, trong hầu hết các trường hợp, chúng phục vụ như một cách để truy cập vào các trường của đối tượng. Nếu bạn muốn làm cho một trường có thể truy cập được vào các lớp khác, bạn có thể thực hiện theo 2 cách.

  1. Làm cho các lĩnh vực là công cộng - không nên.
  2. Sử dụng tính chất.

Đây là một lớp sử dụng các lĩnh vực công cộng.

class Name
{
    public string FullName;
    public int YearOfBirth;
    public int Age;
}

Name name=new Name();

name.FullName="Tim Anderson";
name.YearOfBirth=1979;
name.Age=40;

Mặc dù mã là hoàn toàn hợp lệ, từ quan điểm thiết kế, nó có một số nhược điểm. Vì các trường có thể được đọc và viết, bạn không thể ngăn người dùng viết vào các trường. Bạn có thể áp dụng readonlytừ khóa, nhưng theo cách này, bạn phải khởi tạo các trường chỉ đọc trong hàm tạo. Hơn nữa, không có gì ngăn cản bạn lưu trữ các giá trị không hợp lệ vào các trường của bạn.

name.FullName=null;
name.YearOfBirth=2200;
name.Age=-140;

Mã này là hợp lệ, tất cả các bài tập sẽ được thực hiện mặc dù chúng là phi logic. Agecó giá trị âm, YearOfBirthxa trong tương lai và không tương ứng với Tuổi và không FullNamecó giá trị. Với các trường bạn không thể ngăn người dùng class Namemắc lỗi như vậy.

Đây là một mã với các thuộc tính sửa chữa các vấn đề này.

class Name
{
    private string MFullName="";
    private int MYearOfBirth;

    public string FullName
    {
        get
        {
            return(MFullName);
        }
        set
        {
            if (value==null)
            {
                throw(new InvalidOperationException("Error !"));
            }

            MFullName=value;
        }
    }

    public int YearOfBirth
    {
        get
        {
            return(MYearOfBirth);
        }
        set
        {
            if (MYearOfBirth<1900 || MYearOfBirth>DateTime.Now.Year)
            {
                throw(new InvalidOperationException("Error !"));
            }

            MYearOfBirth=value;
        }
    }

    public int Age
    {
        get
        {
            return(DateTime.Now.Year-MYearOfBirth);
        }
    }

    public string FullNameInUppercase
    {
        get
        {
            return(MFullName.ToUpper());
        }
    }
}

Phiên bản cập nhật của lớp có những ưu điểm sau.

  1. FullNameYearOfBirthđược kiểm tra các giá trị không hợp lệ.
  2. Agekhông thể ghi được. Nó được gọi là từ YearOfBirthvà năm nay.
  3. Một thuộc tính mới FullNameInUppercasechuyển đổi FullNamethành TRƯỜNG HỢP. Đây là một ví dụ nhỏ về sử dụng thuộc tính, trong đó các thuộc tính thường được sử dụng để trình bày các giá trị trường theo định dạng phù hợp hơn với người dùng - ví dụ sử dụng ngôn ngữ hiện tại trên định dạng số cụ thể DateTime.

Bên cạnh đó, các thuộc tính có thể được định nghĩa là ảo hoặc bị ghi đè - đơn giản vì chúng là các phương thức .NET thông thường. Các quy tắc tương tự áp dụng cho các phương thức thuộc tính như đối với các phương thức thông thường.

C # cũng hỗ trợ các bộ chỉ mục là các thuộc tính có tham số chỉ mục trong các phương thức thuộc tính. Dưới đây là ví dụ.

class MyList
{
    private string[]                 MBuffer;

    public MyList()
    {
        MBuffer=new string[100];
    }

    public string this[int Index]
    {
        get
        {
            return(MBuffer[Index]);
        }
        set
        {
            MBuffer[Index]=value;
        }
    }
}

MyList   List=new MyList();

List[10]="ABC";
Console.WriteLine(List[10]);

Vì C # 3.0 cho phép bạn xác định các thuộc tính tự động. Dưới đây là ví dụ.

class AutoProps
{
    public int Value1
    {
        get;
        set;
    }

    public int Value2
    {
        get;
        set;
    }
}

Mặc dù class AutoPropschỉ chứa các thuộc tính (hoặc trông giống như vậy), nó có thể lưu trữ 2 giá trị và kích thước của các đối tượng của lớp này bằng sizeof(Value1)+sizeof(Value2)= 4 + 4 = 8 byte.

Lý do cho điều này là đơn giản. Khi bạn xác định một thuộc tính tự động, trình biên dịch C # tạo mã tự động chứa trường ẩn và thuộc tính có các phương thức thuộc tính truy cập vào trường ẩn này. Đây là trình biên dịch mã sản xuất.

Đây là một mã được tạo bởi ILSpy từ tập hợp đã biên dịch. Lớp chứa các trường và thuộc tính ẩn được tạo.

internal class AutoProps
{
    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private int <Value1>k__BackingField;

    [CompilerGenerated]
    [DebuggerBrowsable(DebuggerBrowsableState.Never)]
    private int <Value2>k__BackingField;

    public int Value1
    {
        [CompilerGenerated]
        get
        {
            return <Value1>k__BackingField;
        }
        [CompilerGenerated]
        set
        {
            <Value1>k__BackingField = value;
        }
    }

    public int Value2
    {
        [CompilerGenerated]
        get
        {
            return <Value2>k__BackingField;
        }
        [CompilerGenerated]
        set
        {
            <Value2>k__BackingField = value;
        }
    }
}

Vì vậy, như bạn có thể thấy, trình biên dịch vẫn sử dụng các trường để lưu trữ các giá trị - vì các trường là cách duy nhất để lưu trữ giá trị vào các đối tượng.

Vì vậy, như bạn có thể thấy, mặc dù các thuộc tính và trường có cú pháp sử dụng tương tự nhưng chúng là các khái niệm rất khác nhau. Ngay cả khi bạn sử dụng các thuộc tính hoặc sự kiện tự động - các trường ẩn được tạo bởi trình biên dịch nơi dữ liệu thực được lưu trữ.

Nếu bạn cần làm cho giá trị trường có thể truy cập được ra thế giới bên ngoài (người dùng của lớp bạn), đừng sử dụng các trường công khai hoặc được bảo vệ. Các lĩnh vực luôn luôn phải được đánh dấu là riêng tư. Các thuộc tính cho phép bạn thực hiện kiểm tra giá trị, định dạng, chuyển đổi, v.v. và nói chung làm cho mã của bạn an toàn hơn, dễ đọc hơn và có thể mở rộng hơn cho các sửa đổi trong tương lai.


4

Ngoài ra, các thuộc tính cho phép bạn sử dụng logic khi đặt giá trị.

Vì vậy, bạn có thể nói rằng bạn chỉ muốn đặt giá trị cho trường số nguyên, nếu giá trị lớn hơn x, nếu không thì ném ngoại lệ.

Tính năng thực sự hữu ích.


4

Nếu bạn định sử dụng các nguyên hàm luồng, bạn buộc phải sử dụng các trường. Thuộc tính có thể phá vỡ mã luồng của bạn. Ngoài ra, những gì cory nói là chính xác.


1
Kể từ khi? khóa trường sao lưu của bạn trong tài sản và đó là tương đương
Sekhat

1
Các thuộc tính là các phương thức và không được nội tuyến bởi bất kỳ CIL JIT nào hiện nay. Nếu bạn định sử dụng các nguyên hàm luồng như Interlocked, bạn cần phải có các trường. Kiểm tra nguồn của bạn. Phải thừa nhận rằng "khóa" là từ sai khi sử dụng.
Jonathan C Dickinson

4

(Đây thực sự nên là một bình luận, nhưng tôi không thể đăng bình luận, vì vậy xin miễn nếu nó không phù hợp như một bài viết).

Tôi đã từng làm việc tại một nơi mà thực tiễn được đề xuất là sử dụng các trường công cộng thay vì các thuộc tính khi def thuộc tính tương đương sẽ chỉ truy cập vào một trường, như trong:

get { return _afield; }
set { _afield = value; }

Lý do của họ là lĩnh vực công cộng có thể được chuyển đổi thành tài sản sau này trong tương lai nếu được yêu cầu. Nó có vẻ hơi lạ đối với tôi vào thời điểm đó. Đánh giá bởi những bài viết này, có vẻ như không nhiều người ở đây sẽ đồng ý. Bạn có thể nói gì để cố gắng thay đổi mọi thứ?

Chỉnh sửa: Tôi nên thêm rằng tất cả các cơ sở mã tại nơi này đã được biên dịch cùng một lúc, vì vậy họ có thể nghĩ rằng việc thay đổi giao diện chung của các lớp (bằng cách thay đổi trường công khai thành thuộc tính) không phải là vấn đề.


Kể từ C # 3.0 , mẫu được mô tả ở đây được hỗ trợ thuận tiện bởi một tính năng có tên là Thuộc tính tự động triển khai .
DavidRR

Tôi nghĩ rằng một trong những lợi thế của C # với Thuộc tính vì chúng có cùng API với các trường, vì vậy khách hàng của lớp không thực sự quan tâm nếu họ đang truy cập vào một thuộc tính hoặc trường. (Điều này KHÔNG đúng trong C ++ chẳng hạn) .. Trong tạo mẫu, tôi nghĩ bắt đầu với các trường công khai, và sau đó di chuyển sang các thuộc tính khi cần thiết. Có một hiệu năng và bộ nhớ đạt được với các thuộc tính, và có thêm gõ. Họ không miễn phí. Nhưng, nếu bạn đổi ý, bạn sẽ không cần cấu trúc lại bất kỳ mã phụ thuộc nào.
Đánh dấu Lakata

Các thuộc tính không thể được sử dụng làm tham số OUT hoặc REF, do đó, việc thay đổi trường thành thuộc tính có thể dẫn đến lỗi biên dịch xuống dòng. Nếu giá trị được triển khai như một thuộc tính ngay từ đầu, thì nó sẽ không bao giờ được sử dụng làm tham số OUT hoặc REF (VAR trong Pascal / Delphi) và mọi thay đổi bạn thực hiện trong getter / setter sẽ rõ ràng đối với việc sử dụng.
HeartWare

4

Về mặt kỹ thuật, tôi không nghĩ rằng có một sự khác biệt, bởi vì các thuộc tính chỉ là các hàm bao quanh các trường do người dùng tạo hoặc tự động tạo bởi trình biên dịch. Mục đích của các thuộc tính là để thực thi đóng gói và cung cấp một tính năng giống như phương thức nhẹ. Đó chỉ là một thực tiễn tồi để tuyên bố các trường là công khai, nhưng nó không có bất kỳ vấn đề nào.


4

Các trường là các biến thành viên thông thường hoặc các thể hiện thành viên của một lớp. Các thuộc tính là một sự trừu tượng để có được và thiết lập các giá trị của chúng . Các thuộc tính cũng được gọi là các hàm truy cập vì chúng cung cấp một cách để thay đổi và truy xuất một trường nếu bạn trưng ra một trường trong lớp là riêng tư. Nói chung, bạn nên khai báo các biến thành viên của mình ở chế độ riêng tư, sau đó khai báo hoặc xác định các thuộc tính cho chúng.

  class SomeClass
  {
     int numbera; //Field

     //Property 
    public static int numbera { get; set;}

  }

3

Các thuộc tính đóng gói các trường, do đó cho phép bạn thực hiện xử lý bổ sung trên giá trị sẽ được đặt hoặc truy xuất. Việc sử dụng các thuộc tính thường là quá mức nếu bạn sẽ không thực hiện bất kỳ quá trình tiền xử lý hoặc hậu xử lý nào trên giá trị trường.


1
không, tôi luôn sử dụng các thuộc tính, nó cho phép bạn linh hoạt thay đổi việc triển khai bất cứ lúc nào mà không phá vỡ API của bạn.
Sekhat

Về tiến hóa API, bạn có thể sử dụng các trường cho dữ liệu riêng tư mà không gặp vấn đề gì. Ngoài ra, trong các trường hợp kỳ lạ khi bạn muốn chia sẻ dữ liệu trong một hội đồng, bạn có thể cấp quyền truy cập 'nội bộ' của các trường.
Daniel Earwicker

3

IMO, Properties chỉ là các cặp chức năng / phương thức / giao diện "SetXXX ()" "GetXXX ()" mà chúng ta đã sử dụng trước đây, nhưng chúng ngắn gọn và thanh lịch hơn.


3

Các trường riêng truyền thống được đặt thông qua các phương thức getter và setter. Vì lợi ích của ít mã hơn, bạn có thể sử dụng các thuộc tính để đặt các trường thay thế.


3

khi bạn có một lớp học là "Xe hơi". Các tính chất là màu sắc, hình dạng ..

Trong đó các trường là các biến được định nghĩa trong phạm vi của một lớp.


3

Từ Wikipedia - Lập trình hướng đối tượng :

Lập trình hướng đối tượng (OOP) là một mô hình lập trình dựa trên khái niệm "đối tượng", là các cấu trúc dữ liệu có chứa dữ liệu dữ liệu, dưới dạng các trường , thường được gọi là thuộc tính; và mã, dưới dạng thủ tục, thường được gọi là phương thức . (nhấn mạnh thêm)

Các thuộc tính thực sự là một phần trong hành vi của đối tượng, nhưng được thiết kế để cung cấp cho người tiêu dùng đối tượng ảo giác / trừu tượng khi làm việc với dữ liệu của đối tượng.


3

Thiết kế của tôi về một trường là một trường chỉ cần được sửa đổi bởi cha mẹ của nó, do đó là lớp. Kết quả là biến trở thành riêng tư, sau đó để có thể cho quyền đọc các lớp / phương thức bên ngoài tôi đi qua hệ thống thuộc tính chỉ với Get. Trường sau đó được lấy bởi thuộc tính và chỉ đọc! Nếu bạn muốn sửa đổi nó, bạn phải thực hiện các phương thức (ví dụ: hàm tạo) và tôi thấy rằng nhờ cách này giúp bạn an toàn, chúng tôi có quyền kiểm soát tốt hơn đối với mã của chúng tôi vì chúng tôi "mặt bích". Người ta rất có thể luôn đặt mọi thứ ở nơi công cộng để mọi trường hợp có thể xảy ra, khái niệm về biến / phương thức / lớp, v.v ... theo tôi chỉ là một sự trợ giúp cho việc phát triển, bảo trì mã. Ví dụ: nếu một người nối lại mã với các trường công khai, anh ta có thể làm bất cứ điều gì và do đó mọi thứ "phi logic" liên quan đến mục tiêu, logic tại sao mã được viết. Đó là quan điểm của tôi.

Khi tôi sử dụng một thuộc tính mô hình cổ điển / thuộc tính chỉ đọc công khai cổ điển, đối với 10 trường riêng tư tôi nên viết 10 thuộc tính công khai! Mã có thể thực sự lớn nhanh hơn. Tôi phát hiện ra setter private và bây giờ tôi chỉ sử dụng các thuộc tính công cộng với setter private. Các setter tạo trong nền một trường riêng.

Đó là lý do tại sao phong cách lập trình cổ điển cũ của tôi là:

public class MyClass
{
 private int _id;
 public int ID { get { return _id; } }
 public MyClass(int id)
 {
  _id = id;
 }
}

Phong cách lập trình mới của tôi:

public class MyClass
{
 public int ID { get; private set; }
 public MyClass(int id)
 {
  ID = id;
 }
}

Vâng, xấu của tôi, xin lỗi!
Tony Pinot

3

Hãy nghĩ về nó: Bạn có một căn phòng và một cánh cửa để vào căn phòng này. Nếu bạn muốn kiểm tra xem ai đang vào và bảo vệ phòng của bạn, thì bạn nên sử dụng tài sản nếu không họ sẽ không phải là bất kỳ ai và mọi người dễ dàng đi vào bất kỳ quy định nào

class Room {
   public string sectionOne;
   public string sectionTwo;
}

Room r = new Room();
r.sectionOne = "enter";

Mọi người đang truy cập vào phầnOne khá dễ dàng, không có bất kỳ kiểm tra nào

class Room 
{
   private string sectionOne;
   private string sectionTwo;

   public string SectionOne 
   {
      get 
      {
        return sectionOne; 
      }
      set 
      { 
        sectionOne = Check(value); 
      }
   }
}

Room r = new Room();
r.SectionOne = "enter";

Bây giờ bạn đã kiểm tra người đó và biết liệu anh ta có điều gì xấu xa với anh ta không


3

Các trường là các biến trong các lớp. Các trường là dữ liệu mà bạn có thể gói gọn thông qua việc sử dụng các công cụ sửa đổi truy cập.

Các thuộc tính tương tự như Trường ở chỗ chúng xác định trạng thái và dữ liệu được liên kết với một đối tượng.

Không giống như một trường, một thuộc tính có một cú pháp đặc biệt kiểm soát cách một người đọc dữ liệu và ghi dữ liệu, chúng được gọi là các toán tử get và set. Logic thiết lập thường có thể được sử dụng để thực hiện xác nhận.


2

Các thuộc tính là loại thành viên lớp đặc biệt, Trong các thuộc tính chúng ta sử dụng phương thức Set hoặc Get được xác định trước. Chúng sử dụng các bộ truy cập thông qua đó chúng ta có thể đọc, viết hoặc thay đổi các giá trị của các trường riêng.

Ví dụ: chúng ta hãy lấy một lớp có tên Employee, với các trường riêng cho tên, tuổi và Employee_Id. Chúng tôi không thể truy cập các trường này từ bên ngoài lớp, nhưng chúng tôi có thể truy cập các trường riêng này thông qua các thuộc tính.

Tại sao chúng ta sử dụng tài sản?

Làm cho trường lớp công khai và phơi bày nó là rủi ro, vì bạn sẽ không kiểm soát được những gì được chỉ định và trả lại.

Để hiểu rõ điều này với một ví dụ, hãy lấy một lớp sinh viên có ID, mật khẩu, tên. Bây giờ trong ví dụ này một số vấn đề với lĩnh vực công cộng

  • ID không nên -ve.
  • Tên không thể được đặt thành null
  • Điểm vượt qua chỉ nên được đọc.
  • Nếu tên sinh viên bị thiếu Không có tên sẽ được trả lại.

Để loại bỏ vấn đề này, chúng tôi sử dụng phương thức Get and set.

// A simple example
public class student
{
    public int ID;
    public int passmark;
    public string name;
}

public class Program
{
    public static void Main(string[] args)
    {
       student s1 = new student();
       s1.ID = -101; // here ID can't be -ve
       s1.Name = null ; // here Name can't be null
    }
}

Bây giờ chúng ta lấy một ví dụ về phương thức get và set

public class student
{
    private int _ID;
    private int _passmark;
    private string_name ;
    // for id property
    public void SetID(int ID)
    {
        if(ID<=0)
        {
            throw new exception("student ID should be greater then 0");
        }
        this._ID = ID;
    }
    public int getID()
    {
        return_ID;
    }
}
public class programme
{
    public static void main()
    {
        student s1 = new student ();
        s1.SetID(101);
    }
    // Like this we also can use for Name property
    public void SetName(string Name)
    {
        if(string.IsNullOrEmpty(Name))
        {
            throw new exeception("name can not be null");
        }
        this._Name = Name;
    }
    public string GetName()
    {
        if( string.IsNullOrEmpty(This.Name))
        {
            return "No Name";
        }
        else
        {
            return this._name;
        }
    }
        // Like this we also can use for Passmark property
    public int Getpassmark()
    {
        return this._passmark;
    }
}

2

Thông tin bổ sung: Theo mặc định, các bộ truy cập get và set có thể truy cập như chính thuộc tính. Bạn có thể kiểm soát / hạn chế khả năng truy cập của người truy cập riêng lẻ (để lấy và đặt) bằng cách áp dụng các công cụ sửa đổi truy cập hạn chế hơn trên chúng.

Thí dụ:

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

Ở đây get vẫn được truy cập công khai (vì thuộc tính là công khai), nhưng thiết lập được bảo vệ (một công cụ xác định truy cập hạn chế hơn).


2

Thuộc tính được sử dụng để lộ trường. Họ sử dụng các bộ truy cập (set, get) thông qua đó các giá trị của các trường riêng có thể được đọc, viết hoặc thao tác.

Thuộc tính không đặt tên cho các vị trí lưu trữ. Thay vào đó, họ có những người truy cập đọc, viết hoặc tính toán các giá trị của họ.

Sử dụng các thuộc tính, chúng ta có thể đặt xác thực cho loại dữ liệu được đặt trên một trường.

Ví dụ: chúng ta có tuổi nguyên trường riêng trên đó chúng ta nên cho phép các giá trị dương vì tuổi không thể âm.

Chúng ta có thể làm điều này theo hai cách sử dụng getter và setters và sử dụng thuộc tính.

 Using Getter and Setter

    // field
    private int _age;

    // setter
    public void set(int age){
      if (age <=0)
       throw new Exception();

      this._age = age;
    }

    // getter
    public int get (){
      return this._age;
    }

 Now using property we can do the same thing. In the value is a key word

    private int _age;

    public int Age{
    get{
        return this._age;
    }

    set{
       if (value <= 0)
         throw new Exception()
       }
    }

Thuộc tính được triển khai tự động nếu chúng ta không logic trong việc lấy và đặt người truy cập, chúng ta có thể sử dụng thuộc tính được triển khai tự động.

Khi bạn biên dịch thuộc tính tự động thực hiện sẽ tạo một trường riêng, ẩn danh chỉ có thể được truy cập thông qua các bộ truy cập get và set.

public int Age{get;set;}

Các thuộc tính trừu tượng Một lớp trừu tượng có thể có một thuộc tính trừu tượng, cần được thực hiện trong lớp dẫn xuất

public abstract class Person
   {
      public abstract string Name
      {
         get;
         set;
      }
      public abstract int Age
      {
         get;
         set;
      }
   }

// overriden something like this
// Declare a Name property of type string:
  public override string Name
  {
     get
     {
        return name;
     }
     set
     {
        name = value;
     }
  }

Chúng ta có thể đặt riêng một thuộc tính Trong phần này, chúng ta có thể đặt riêng thuộc tính tự động (được đặt trong lớp)

public int MyProperty
{
    get; private set;
}

Bạn có thể đạt được tương tự với mã này. Trong tính năng thiết lập thuộc tính này không khả dụng vì chúng ta phải đặt giá trị trực tiếp cho trường.

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

2

Phần lớn các trường hợp nó sẽ là một tên thuộc tính mà bạn truy cập trái ngược với tên biến ( trường ) Lý do cho điều đó được coi là thực hành tốt trong .NET và đặc biệt là trong C # để bảo vệ mọi phần dữ liệu trong một lớp , cho dù đó là biến đối tượng hay biến tĩnh (biến lớp) vì nó được liên kết với một lớp.

Bảo vệ tất cả các biến có đặc tính tương ứng cho phép bạn xác định, thiết lập và nhận được accessors và làm những việc như xác nhận khi bạn đang thao tác những mẩu dữ liệu.

Nhưng trong các trường hợp khác như lớp Math (không gian tên hệ thống), có một vài thuộc tính tĩnh được tích hợp vào lớp. Một trong số đó là hằng số toán học PI

ví dụ. Toán.PI

và vì PI là một phần dữ liệu được xác định rõ ràng, chúng tôi không cần phải có nhiều bản sao PI, nên nó sẽ luôn có cùng giá trị. Vì vậy, các biến tĩnh đôi khi được sử dụng để chia sẻ dữ liệu giữa các đối tượng của một lớp, nhưng chúng cũng thường được sử dụng cho thông tin không đổi trong đó bạn chỉ cần một bản sao của một phần dữ liệu.

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.