ความแตกต่างระหว่างเขตข้อมูลและคุณสมบัติคืออะไร?


1130

ใน C # อะไรทำให้ฟิลด์แตกต่างจากคุณสมบัติและเมื่อใดควรใช้ฟิลด์ใดแทนคุณสมบัติ


32
ไมโครซอฟท์โดยตรงตอบคำถามนี้ (NET ภาษาทั้งหมด) เป็นส่วนหนึ่งของแนวทางการออกแบบสมาชิก สำหรับรายละเอียดโปรดดูบทความการออกแบบสถานที่ให้บริการและการออกแบบสนาม หมายเหตุมีความแตกต่างระหว่างสมาชิกอินสแตนซ์และสมาชิกคงที่
DavidRR

คำตอบ:


979

คุณสมบัติเปิดเผยฟิลด์ ฟิลด์ควร (เกือบตลอดเวลา) ควรเก็บเป็นส่วนตัวในชั้นเรียนและเข้าถึงได้ผ่านทางรับและตั้งค่าคุณสมบัติ พร็อพเพอร์ตี้ให้ระดับของนามธรรมที่ทำให้คุณสามารถเปลี่ยนฟิลด์ในขณะที่ไม่มีผลกระทบต่อวิธีการเข้าถึงจากภายนอกโดยสิ่งต่าง ๆ ที่ใช้คลาสของคุณ

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 ชี้ให้เห็นว่าคุณสมบัติไม่จำเป็นต้องมีแค็ปซูลฟิลด์พวกเขาสามารถทำการคำนวณในฟิลด์อื่นหรือใช้เพื่อวัตถุประสงค์อื่น

@GSS ชี้ให้เห็นว่าคุณยังสามารถทำตรรกะอื่น ๆ เช่นการตรวจสอบเมื่อมีการเข้าถึงคุณสมบัติคุณสมบัติอื่นที่มีประโยชน์


185
เป็นที่น่าสังเกตว่าคุณสมบัติไม่จำเป็นต้องมีแค็ปซูลฟิลด์ อาจไม่มีฟิลด์อยู่ด้านหลังที่พัก มันอาจเป็นการคำนวณหรือคืนค่าคงที่หรืออะไรก็ตาม
Kent Boogaart

9
"ในขณะที่ไม่ส่งผลกระทบต่อวิธีการเข้าถึงจากภายนอกโดยสิ่งต่าง ๆ ที่ใช้ในชั้นเรียนของคุณ" ยกโทษให้ฉันถ้าฉันเข้าใจไม่ถูกต้อง แต่ทำไมความจำเป็นในการปรับเปลี่ยนการเข้าถึงต่อหน้าคุณสมบัติเลยถ้าฟิลด์ด้านหลังดูเหมือนว่าจะจัดการกับเรื่องนี้? นั่นคือเหตุผลที่ทำให้ทรัพย์สินใด ๆ ที่ไม่ใช่สาธารณะ?
Chucky

18
คำตอบของคุณถูกต้องก่อนการแก้ไขและความคิดเห็นที่ไม่ถูกต้อง upvote อย่างผิดปกติ สถานที่ให้บริการควรห่อหุ้มหนึ่งหรือมากกว่าหนึ่งเขตข้อมูลและไม่ควรทำการยกหรือการตรวจสอบอย่างหนัก หากคุณต้องการคุณสมบัติเช่นชื่อผู้ใช้หรือรหัสผ่านที่จะมีการตรวจสอบการเปลี่ยนชนิดของพวกเขาจากสตริงเพื่อวัตถุราคา มีสัญญาที่ไม่ได้พูดระหว่างผู้สร้างคลาสกับผู้บริโภค ฟิลด์ยึดสถานะคุณสมบัติเปิดเผยสถานะโดยใช้หนึ่งหรือมากกว่าหนึ่งช่องเปลี่ยนสถานะ (ยกของหนัก) และฟังก์ชั่นดำเนินการค้นหา (ยกของหนัก) นี่ไม่ใช่หินแค่คาดหวังที่หลวม
Suamere

6
@jpaugh หากฉันเป็นผู้บริโภคในชั้นเรียนฉันทำตามสัญญาที่กำหนดโดยผู้สร้างชั้นเรียน หากทรัพย์สินเป็นstringสัญญาของฉันคือ: กำหนดตัวอักษรใด ๆ ได้สูงสุด ~ 2bil ความยาว หากทรัพย์สินเป็นDateTimeสัญญาของฉันคือ: กำหนดหมายเลขใด ๆ ภายในขีด จำกัด ของ DateTime ซึ่งฉันสามารถค้นหาได้ หากผู้สร้างเพิ่มข้อ จำกัด ให้กับผู้ตั้งค่าข้อ จำกัด เหล่านั้นจะไม่ถูกสื่อสาร แต่ถ้าแทนผู้สร้างการเปลี่ยนแปลงชนิดจากstringไปSurnameแล้วระดับนามสกุลใหม่ของพวกเขาสื่อสาร จำกัด และทรัพย์สินที่public Surname LastNameไม่ได้มีการตรวจสอบการตั้งค่า นอกจากนี้ยังSurnameสามารถนำมาใช้ซ้ำได้
Suamere

4
และเนื่องจากSurnameในตัวอย่างของฉันสามารถนำกลับมาใช้ใหม่ได้คุณไม่จำเป็นต้องกังวลเกี่ยวกับการคัดลอก / วางการตรวจสอบความถูกต้องเหล่านั้นใน setter คุณสมบัติไปยังสถานที่อื่น ๆ ในรหัส หรือสงสัยว่าการตรวจสอบความถูกต้องของนามสกุลอยู่ในหลาย ๆ ที่หรือไม่หากคุณทำการเปลี่ยนแปลงกฎธุรกิจสำหรับนามสกุล ลองดูลิงค์ที่ฉันโพสต์เกี่ยวกับ Value Objects
Suamere

261

หลักการเขียนโปรแกรมเชิงวัตถุบอกว่าการทำงานภายในของชั้นเรียนควรถูกซ่อนไว้จากโลกภายนอก หากคุณเปิดเผยช่องที่คุณกำลังสำคัญเปิดเผยการใช้งานภายในของชั้นเรียน ดังนั้นเราจึงห่อฟิลด์ด้วยคุณสมบัติ (หรือวิธีการในกรณีของ Java) เพื่อให้เราสามารถเปลี่ยนการใช้งานโดยไม่ต้องทำลายรหัสขึ้นอยู่กับเรา การเห็นว่าเราสามารถใส่ตรรกะในคุณสมบัติยังช่วยให้เราสามารถทำการตรวจสอบตรรกะ ฯลฯ หากเราต้องการ C # 3 มีความคิดที่สับสนเกี่ยวกับ autoproperties สิ่งนี้ช่วยให้เราสามารถกำหนด Property และคอมไพเลอร์ C # 3 จะสร้างฟิลด์ส่วนตัวสำหรับเรา

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 สำหรับการกล่าวถึง autoproperties - ฉันคิดว่านี่เป็นสิ่งที่หลายคำตอบที่นี่ (และที่อื่น ๆ ) ลืมที่จะนำเข้ามาโดยไม่มีคำอธิบายนี้มันยังคงค่อนข้างยากที่จะเข้าใจสิ่งที่เป็นpublic int myVar { get; set; }จริง (และฉันคิดว่ามันเป็นเหตุผล เป็นเวลาอย่างน้อย 50% ของความนิยมที่คำถามนี้ได้รับ)
Priidu Neemre

7
+1 สำหรับการกล่าวถึงรถยนต์และกล่าวถึงวิธีการทำงาน ("AutoProperty สร้างช่องส่วนตัวสำหรับเรา") นี่คือคำตอบที่ฉันได้มองหาสำหรับคำถามที่ฉันมี เมื่อทำการวิจัยฉันไม่เห็นหน้า MSDN เกี่ยวกับพวกเขาบ่งชี้ว่ามีการสร้างเขตข้อมูลส่วนตัวและทำให้เกิดความสับสน ฉันเดาว่านั่นหมายถึงอะไร "คุณสมบัติที่ได้รับอนุญาตในคุณสมบัติที่ดำเนินการโดยอัตโนมัติ แต่ไม่ชัดเจนในเขตข้อมูลสำรองเนื่องจากสิ่งเหล่านี้ไม่สามารถเข้าถึงได้จากซอร์สโค้ดของคุณหากคุณต้องใช้แอตทริบิวต์ในฟิลด์สำรองของคุณสมบัติให้สร้างคุณสมบัติปกติ" แต่ไม่แน่ใจ
Nyra

3
โปรดทราบว่าตัวอย่างที่กำหนดไม่ได้ห่อหุ้มหมอบ คุณสมบัตินี้ให้การเข้าถึงแบบเต็ม 100% ไปยังเขตข้อมูลส่วนตัวดังนั้นจึงไม่ได้มุ่งเน้นวัตถุทั้งหมด คุณอาจมีเขตข้อมูลสาธารณะในกรณีนี้เช่นกัน ได้รับจะช่วย (เล็กน้อย) เพื่อ refactor รหัสในอนาคต แต่ IDE ใด ๆ ที่คุ้มค่าความกล้าหาญสามารถเปลี่ยนเขตข้อมูลให้เป็นสถานที่ให้บริการที่มีการกดแป้นพิมพ์ไม่กี่ คำตอบอาจถูกต้องทางเทคนิคเกี่ยวกับการทำงานของคุณสมบัติ แต่ไม่ได้ให้คำอธิบายที่ดีสำหรับการใช้งานของพวกเขา
sara

2
@ ไก่ฉันยอมรับว่าคำตอบง่ายกว่าและไม่ได้แสดงพลังทั้งหมดของสถานที่ให้บริการอัตโนมัติ แต่ฉันไม่เห็นด้วยกับที่นี่ไม่ใช่เชิงวัตถุ คุณอาจต้องการที่จะตรวจสอบความแตกต่างระหว่างเขตข้อมูลและคุณสมบัติ ฟิลด์ไม่สามารถเป็นเสมือนจริงและvirtualตัวมันเองเป็นส่วนหนึ่งของการเขียนโปรแกรมเชิงวัตถุ
Gobe

จริงอยู่ที่การทำงานมีความแตกต่าง ฉันจะไม่เรียกvirtualOOP ต่อทุกครั้ง มันเป็นเครื่องมือที่เปิดใช้งาน polymorphism ซึ่งเป็นหนึ่งในเครื่องมือสำคัญที่ทำให้ ENOPLES OOP มันไม่ได้เป็นของ OOP และของตัวเองและไม่มีอะไรโดยเนื้อแท้ OOP เกี่ยวกับ autoproperty สาธารณะ ฉันจะไม่นับสิ่งต่าง ๆ เช่นภาพสะท้อนหรือการบันทึกข้อมูล OOP ที่เกี่ยวข้องเช่นกัน โดยปกติฉันจะไม่พูดถึงมันมากนัก แต่คำตอบที่กล่าวถึงหลักการ OO เป็นแรงผลักดันที่อยู่เบื้องหลังตัวอย่างโค้ดและฉันไม่เห็นด้วย
ร่า

164

ความแตกต่างที่สำคัญคืออินเตอร์เฟสสามารถมีคุณสมบัติได้ แต่ไม่ใช่ฟิลด์ สิ่งนี้สำหรับฉันขีดเส้นใต้ว่าควรใช้คุณสมบัติเพื่อกำหนดอินเทอร์เฟซสาธารณะของคลาสในขณะที่ฟิลด์นั้นมีไว้เพื่อใช้ในการทำงานส่วนตัวภายในของคลาส ตามกฎแล้วฉันไม่ค่อยสร้างเขตข้อมูลสาธารณะและในทำนองเดียวกันฉันไม่ค่อยสร้างคุณสมบัติที่ไม่ใช่แบบสาธารณะ


98

ฉันจะให้คุณสองสามตัวอย่างของการใช้คุณสมบัติที่อาจเปลี่ยนเกียร์:

  • การเริ่มต้น Lazy :หากคุณมีคุณสมบัติของวัตถุที่มีราคาแพง แต่ไม่สามารถเข้าถึงได้ทั้งหมดในการรันโค้ดปกติคุณสามารถหน่วงเวลาการโหลดผ่านคุณสมบัติได้ ด้วยวิธีนี้มันแค่นั่งอยู่ที่นั่น แต่ครั้งแรกที่โมดูลอื่นพยายามเรียกคุณสมบัตินั้นมันจะตรวจสอบว่าเขตข้อมูลพื้นฐานเป็นโมฆะหรือไม่ถ้าเป็นมันจะไปข้างหน้าและโหลดโดยไม่ทราบว่าโมดูลการโทรนั้น สิ่งนี้สามารถเร่งความเร็วการเริ่มต้นวัตถุได้อย่างมาก
  • การติดตามสกปรก:ซึ่งฉันได้เรียนรู้จากคำถามของฉันเองที่นี่ใน StackOverflow เมื่อฉันมีวัตถุจำนวนมากที่ค่าอาจมีการเปลี่ยนแปลงในระหว่างการทำงานฉันสามารถใช้คุณสมบัติเพื่อติดตามว่าพวกเขาจำเป็นต้องบันทึกกลับไปยังฐานข้อมูลหรือไม่ หากไม่ใช่คุณสมบัติเดียวของวัตถุที่เปลี่ยนแปลงสถานะของ IsDirty จะไม่สะดุดและฟังก์ชันการบันทึกจะข้ามไปเมื่อทำการตัดสินใจว่าจะต้องกลับไปที่ฐานข้อมูล

1
คำถามเกี่ยวกับการติดตามที่สกปรก: ถ้าฉันสามารถเปลี่ยนฟิลด์ได้โดยตรง - ฉันไม่รู้ว่าสามารถทำได้หรือไม่ฉันสามารถพูดได้ว่า: "ไม่จำเป็นต้องบันทึกวัตถุหากไม่ได้เปลี่ยน FIELD ของวัตถุเดียว" ดังนั้นการติดตามที่สกปรกจะไม่แตกต่างกัน
ไซต์

2
@juanpastas: ข้อดีของคุณสมบัติที่เกี่ยวข้องกับการติดตามสกปรกคือถ้าผู้ตั้งค่าคุณสมบัติจะตั้งค่าสถานะ "สกปรก" ดังนั้นในสถานการณ์ที่ไม่มีการตั้งค่ารหัสธงจะไม่ต้องตรวจสอบค่าของคุณสมบัติใด ๆ เพื่อดู ถ้าพวกเขาอาจมีการเปลี่ยนแปลง ในทางตรงกันข้ามหากวัตถุเปิดเผยคุณลักษณะเป็นเขตข้อมูลเนื้อหาของเขตข้อมูลทั้งหมดจะต้องเปรียบเทียบกับค่าก่อนหน้า (ซึ่งไม่เพียงเพิ่มเวลาในการทำการเปรียบเทียบ แต่ยังหมายถึงรหัสต้องมีค่าก่อนหน้า)
supercat

นี่เป็นวิธีที่ดีนอกจากนี้ยังช่วยให้คุณสามารถเรียกใช้เมธอด (เป็นเหตุการณ์) หรือบันทึกเมื่อตั้งค่าหรืออ่าน
coloboxp

54

ใช้คุณสมบัติคุณสามารถเพิ่มเหตุการณ์เมื่อค่าของคุณสมบัติมีการเปลี่ยนแปลง (aka. PropertyChangedEvent) หรือก่อนที่ค่าจะเปลี่ยนเพื่อรองรับการยกเลิก

ไม่สามารถทำได้ด้วยช่อง (เข้าถึงโดยตรงไปยัง)

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
ฉันใช้เวลานานในการค้นหาสิ่งนี้ นี่คือMVVM ขอบคุณ ! :)

46

เนื่องจากหลายคนได้อธิบายกับผู้เชี่ยวชาญด้านเทคนิคและข้อเสียของPropertiesและFieldถึงเวลาที่จะได้รับตัวอย่างเรียลไทม์

1. คุณสมบัติช่วยให้คุณตั้งค่าระดับการเข้าถึงแบบอ่านอย่างเดียว

พิจารณากรณีdataTable.Rows.CountและdataTable.Columns[i].Caption. พวกเขามาจากชั้นเรียนDataTableและเป็นที่เปิดเผยแก่เรา ความแตกต่างในระดับการเข้าถึงข้อมูลสำหรับพวกเขาคือเราไม่สามารถกำหนดค่าเป็นdataTable.Rows.Countแต่เราสามารถอ่านและเขียนdataTable.Columns[i].Captionได้ เป็นไปได้ที่ผ่านField? ไม่มี !!! สิ่งนี้สามารถทำได้ด้วยPropertiesเท่านั้น

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. คุณสมบัติใน PropertyGrid

คุณอาจทำงานกับButtonใน Visual Studio คุณสมบัติของมันจะแสดงในสิ่งที่PropertyGridคล้ายTextกันNameฯลฯ เมื่อเราลากและวางปุ่มและเมื่อเราคลิกคุณสมบัติมันจะค้นหาคลาสButtonและตัวกรองโดยอัตโนมัติPropertiesและแสดงว่าในPropertyGrid(ซึ่งPropertyGridจะไม่แสดงFieldแม้ว่าจะเป็นสาธารณะ)

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;
        }
   } 

ในPropertyGrid, คุณสมบัติNameและTextจะแสดง SomePropertyแต่ไม่ ทำไม??? เนื่องจากคุณสมบัติสามารถรับคุณสมบัติได้ มันจะไม่แสดงในกรณีที่[Browsable(false)]เป็นเท็จ

3. สามารถดำเนินการคำสั่งภายในคุณสมบัติ

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. สามารถใช้คุณสมบัติได้เฉพาะในแหล่งข้อมูลการผูก

Binding Sourceช่วยให้เราลดจำนวนบรรทัดของรหัส ยังไม่ได้รับการยอมรับจากFields BindingSourceเราควรใช้Propertiesเพื่อสิ่งนั้น

5. โหมดแก้ไขข้อบกพร่อง

พิจารณาว่าเรากำลังใช้Fieldเพื่อเก็บค่า ในบางจุดเราจำเป็นต้องตรวจแก้จุดบกพร่องและตรวจสอบว่าค่าที่ได้รับเป็นโมฆะสำหรับเขตข้อมูลนั้น มันจะเป็นเรื่องยากที่จะทำซึ่งมีจำนวนบรรทัดของรหัสที่มีมากกว่า 1000 ในสถานการณ์เช่นนี้เราสามารถใช้และสามารถตั้งค่าการแก้ปัญหาภายในโหมดPropertyProperty

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

สิ่งเหล่านี้เป็นข้อเท็จจริงที่น่าสนใจ แต่คุณกำลังพลาดจุดของทุ่งนาและปรัชญาคุณสมบัติ
David Ferenczy Rogožan

Philisophyคุณหมายถึงอะไร? @Dawid Ferenczy
Sarath Avanavu

ดูตัวอย่างคำตอบที่ทำเครื่องหมายไว้ แต่คุณสังเกตเห็นว่าคุณเพียงแค่แสดงตัวอย่างการใช้งานเนื่องจากความแตกต่างระหว่างฟิลด์และคุณสมบัติได้ถูกอธิบายไว้แล้วดังนั้นลืมความคิดเห็นของฉันโปรด :)
David Ferenczy Rogožan

2
อ่านประโยคแรกของฉันในคำตอบของฉัน ฉันบอกโดยเฉพาะว่าฉันจะไม่ทำซ้ำทุกอย่างอีกครั้งที่นี่ ไม่สมเหตุสมผล !!! ผู้คนจะดูคำอธิบายก่อนจากนั้นจึงเป็นตัวอย่าง คำตอบที่ทำเครื่องหมายไว้ให้คำอธิบายที่ดี แต่ฉันเพิ่มกับสถานการณ์และตัวอย่างแบบเรียลไทม์ที่เหมาะสม โปรดตรวจสอบให้แน่ใจว่าคุณคิดจากมุมมองของผู้อ่านก่อนที่จะแสดงความคิดเห็น @Dawid Ferenczy
Sarath Avanavu

1
ฉันได้อ่านมัน แต่คุณไม่ได้อ่านความคิดเห็นของฉันก่อนหน้าอย่างเห็นได้ชัด " แต่คุณสังเกตเห็นว่าคุณเพียงแค่ให้ตัวอย่างการใช้งานที่แตกต่างกันตั้งแต่ระหว่างเขตข้อมูลและคุณสมบัติได้รับการอธิบายไว้แล้วเพื่อให้ลืมความคิดเห็นของฉันโปรด :) " .
David Ferenczy Rogožan

32

ความแตกต่าง - ใช้ (เมื่อใดและทำไม)

ฟิลด์เป็นตัวแปรที่มีการประกาศโดยตรงในชั้นเรียนหรือ struct คลาสหรือโครงสร้างอาจมีฟิลด์อินสแตนซ์หรือฟิลด์สแตติกหรือทั้งสองอย่าง โดยทั่วไปแล้วคุณควรใช้เขตข้อมูลเฉพาะสำหรับตัวแปรที่มีการเข้าถึงส่วนตัวหรือการป้องกัน ข้อมูลที่คลาสของคุณเปิดเผยถึงรหัสลูกค้าควรได้รับการจัดเตรียมผ่านวิธีการคุณสมบัติและตัวทำดัชนี โดยใช้โครงสร้างเหล่านี้สำหรับการเข้าถึงเขตข้อมูลภายในโดยอ้อมคุณสามารถป้องกันค่าอินพุตที่ไม่ถูกต้อง

คุณสมบัติเป็นสมาชิกที่ให้กลไกที่มีความยืดหยุ่นในการอ่านเขียนหรือการคำนวณมูลค่าของข้อมูลส่วนตัว คุณสมบัติสามารถใช้เป็นหากพวกเขาเป็นประชาชนสมาชิกข้อมูล แต่พวกเขาเป็นจริงวิธีการพิเศษที่เรียกว่าaccessors ทำให้สามารถเข้าถึงข้อมูลได้ง่ายและยังช่วยส่งเสริมความปลอดภัยและความยืดหยุ่นของวิธีการต่างๆ คุณสมบัติช่วยให้คลาสเปิดเผยวิธีการรับและการตั้งค่าสาธารณะในขณะที่ซ่อนการใช้งานหรือรหัสยืนยัน get access accessor ใช้เพื่อส่งคืนค่าคุณสมบัติและชุด access access ถูกใช้เพื่อกำหนดค่าใหม่


นี่เป็นคำตอบที่ยอดเยี่ยมจริงๆช่วยให้ฉันเข้าใจสิ่งนี้
Steve Bauman

14

คุณสมบัติมีข้อได้เปรียบหลักในการอนุญาตให้คุณเปลี่ยนวิธีการเข้าถึงข้อมูลบนวัตถุโดยไม่ทำลายส่วนต่อประสานสาธารณะ ตัวอย่างเช่นถ้าคุณต้องการเพิ่มการตรวจสอบพิเศษหรือเปลี่ยนเขตข้อมูลที่เก็บไว้ในการคำนวณคุณสามารถทำได้อย่างง่ายดายหากคุณเปิดเผยเขตข้อมูลเป็นคุณสมบัติครั้งแรก หากคุณเพิ่งเปิดดูฟิลด์โดยตรงคุณจะต้องเปลี่ยนอินเทอร์เฟซสาธารณะของชั้นเรียนของคุณเพื่อเพิ่มฟังก์ชั่นใหม่ การเปลี่ยนแปลงนั้นจะทำให้ลูกค้าที่มีอยู่เดิมต้องการให้พวกเขาคอมไพล์ใหม่ก่อนจึงจะสามารถใช้รหัสรุ่นใหม่ของคุณได้

หากคุณเขียนไลบรารีคลาสที่ออกแบบมาเพื่อการใช้งานที่กว้างขวาง (เช่น. NET Framework ซึ่งถูกใช้โดยผู้คนหลายล้านคน) นั่นอาจเป็นปัญหาได้ อย่างไรก็ตามหากคุณกำลังเขียนคลาสที่ใช้ภายในโค้ดเบสขนาดเล็ก (พูดว่า <= 50 K บรรทัด) มันไม่ใช่เรื่องใหญ่จริง ๆ เพราะไม่มีใครจะได้รับผลกระทบจากการเปลี่ยนแปลงของคุณ ในกรณีนั้นจริงๆมันเป็นเพียงการตั้งค่าส่วนตัว


11

คุณสมบัติรองรับการเข้าถึงแบบอสมมาตรเช่นคุณสามารถมีทั้งตัวรับและตัวตั้งค่าหรือเพียงหนึ่งในสองตัว คุณสมบัติในทำนองเดียวกันสนับสนุนการเข้าถึงส่วนบุคคลสำหรับ getter / setter เขตข้อมูลมีความสมมาตรอยู่เสมอนั่นคือคุณสามารถรับและตั้งค่าได้ตลอดเวลา ข้อยกเว้นนี้คือฟิลด์แบบอ่านอย่างเดียวซึ่งไม่สามารถตั้งค่าได้อย่างชัดเจนหลังจากการกำหนดค่าเริ่มต้น

คุณสมบัติอาจทำงานเป็นเวลานานมีผลข้างเคียงและอาจทำให้เกิดข้อยกเว้น เขตข้อมูลมีความรวดเร็วโดยไม่มีผลข้างเคียงและจะไม่มีข้อยกเว้น เนื่องจากผลข้างเคียงคุณสมบัติอาจส่งคืนค่าที่แตกต่างกันสำหรับการโทรแต่ละครั้ง (เช่นในกรณีของ DateTime.Now เช่น DateTime.Now ไม่เท่ากับ DateTime.Now เสมอ) ฟิลด์คืนค่าเดิมเสมอ

ฟิลด์อาจใช้สำหรับพารามิเตอร์ out / ref คุณสมบัติอาจไม่ คุณสมบัติสนับสนุนตรรกะเพิ่มเติม - ซึ่งสามารถใช้เพื่อดำเนินการโหลดแบบสันหลังยาวในหมู่สิ่งอื่น ๆ

คุณสมบัติสนับสนุนระดับของนามธรรมโดยการห่อหุ้มสิ่งที่มันหมายถึงการได้รับ / การตั้งค่า

ใช้คุณสมบัติในกรณีส่วนใหญ่ / ทั้งหมด แต่พยายามหลีกเลี่ยงผลข้างเคียง


เขตข้อมูลอาจมีปัญหาด้านต้นทุนทั้งหมดของคุณสมบัติเมื่อชนิดข้อมูลของเขตข้อมูลเป็นวัตถุที่มีตัวดำเนินการแปลงเกินพิกัดซึ่งเป็น gotcha ที่บอบบาง
Andy Dent

1
คุณสมบัติไม่ควรมีผลข้างเคียง แม้แต่ตัวดีบั๊กก็ยังสามารถประเมินได้อย่างปลอดภัย
Craig Gidney

@ Strilanc: ฉันเห็นด้วยอย่างสมบูรณ์ แต่นั่นไม่ใช่กรณีเสมอไป สำหรับตัวดีบักนั้นมีปัญหามากมายเกี่ยวกับ FuncEval ถ้านั่นคือสิ่งที่คุณกำลังพูด
Brian Rasmussen

11

ในพื้นหลังคุณสมบัติจะรวบรวมเป็นวิธีการ ดังนั้นNameคุณสมบัติเรียบเรียงและget_Name() set_Name(string value)คุณสามารถเห็นสิ่งนี้หากคุณศึกษารหัสรวบรวม ดังนั้นจึงมีค่าใช้จ่ายเล็ก ๆ (มาก) ของประสิทธิภาพเมื่อใช้งาน โดยปกติคุณจะใช้คุณสมบัติเสมอหากคุณเปิดเผยฟิลด์ไปยังภายนอกและคุณมักจะใช้ภายในหากคุณต้องการตรวจสอบความถูกต้องของค่า


7

เมื่อคุณต้องการให้ตัวแปรส่วนตัว (ฟิลด์) ของคุณสามารถเข้าถึงวัตถุของคลาสจากคลาสอื่นคุณต้องสร้างคุณสมบัติสำหรับตัวแปรเหล่านั้น

ตัวอย่างเช่นถ้าฉันมีตัวแปรชื่อเป็น "id" และ "name" ซึ่งเป็นส่วนตัว แต่อาจมีสถานการณ์ที่ตัวแปรนี้จำเป็นสำหรับการดำเนินการอ่าน / เขียนนอกชั้นเรียน ในสถานการณ์นั้นคุณสมบัติสามารถช่วยให้ฉันรับตัวแปรนั้นเพื่ออ่าน / เขียนขึ้นอยู่กับการรับ / กำหนดที่กำหนดไว้สำหรับคุณสมบัติ คุณสมบัติสามารถเป็นแบบอ่านอย่างเดียว / เขียนอย่างเดียว / อ่านได้ทั้งสองอย่าง

นี่คือตัวอย่าง

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

คำถามที่สองที่นี่ "เมื่อควรสนามนำมาใช้แทนของทรัพย์สินหรือไม่?" สัมผัสเพียงสั้น ๆ ในคำตอบอื่น ๆ นี้และครับอันนี้เกินไปแต่ไม่ได้รายละเอียดมากจริงๆ

โดยทั่วไปแล้วคำตอบอื่น ๆ ทั้งหมดเป็นแบบเฉพาะจุดเกี่ยวกับการออกแบบที่ดี: ชอบการเปิดเผยคุณสมบัติมากกว่าการเปิดเผยฟิลด์ ในขณะที่คุณอาจจะไม่สม่ำเสมอพบว่าตัวเองพูดว่า "ว้าว, จินตนาการเท่าใดสิ่งที่เลวร้ายจะเป็นถ้าฉันได้ทำนี้ฟิลด์แทนของสถานที่ให้บริการ" ก็มากหายากมากขึ้นที่จะคิดว่าสถานการณ์ที่คุณจะพูดว่า "ว้าวที่ ขอบคุณพระเจ้าที่ฉันใช้ฟิลด์ที่นี่แทนอสังหาริมทรัพย์ "

แต่มีข้อดีอย่างหนึ่งที่เขตข้อมูลมีคุณสมบัติมากกว่าและนั่นคือความสามารถในการใช้เป็นพารามิเตอร์ "ref" / "out" สมมติว่าคุณมีวิธีการที่มีลายเซ็นดังต่อไปนี้:

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

และสมมติว่าคุณต้องการใช้วิธีการนั้นในการแปลงอาเรย์ที่สร้างขึ้นเช่นนี้

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

นี่ฉันคิดว่าวิธีที่เร็วที่สุดที่จะทำเพราะXและYเป็นคุณสมบัติ:

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;
}

และนั่นจะเป็นสิ่งที่ดีทีเดียว! ถ้าคุณไม่มีการวัดที่พิสูจน์เป็นอย่างอื่นไม่มีเหตุผลที่จะทำให้เหม็น แต่ฉันเชื่อว่ามันไม่ได้รับประกันว่าจะเร็วเท่านี้:

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);
}

การทำการวัดบางอย่างด้วยตัวเองรุ่นที่มีเขตข้อมูลใช้เวลาประมาณ 61% ของเวลาเป็นรุ่นที่มีคุณสมบัติ (.NET 4.6, Windows 7, x64, โหมดการเปิดตัวโดยไม่มีการเชื่อมต่อดีบักเกอร์) ยิ่งTransformPointวิธีการมีราคาแพงเท่าใดก็ยิ่งมีความแตกต่างน้อยลงเท่านั้น หากต้องการทำซ้ำด้วยตัวคุณเองให้รันด้วยบรรทัดแรกที่แสดงความคิดเห็นและไม่ได้แสดงความคิดเห็น

แม้ว่าจะไม่มีประโยชน์ด้านประสิทธิภาพสำหรับข้างต้น แต่ก็มีสถานที่อื่นที่สามารถใช้พารามิเตอร์ ref และ out อาจเป็นประโยชน์เช่นเมื่อเรียกเมธอดตระกูล InterlockedหรือVolatile หมายเหตุ: ในกรณีนี้เป็นสิ่งใหม่สำหรับคุณความผันผวนนั้นเป็นวิธีการที่จะได้รับพฤติกรรมเดียวกับที่ให้ไว้โดยvolatileคำหลัก เช่นนี้volatileมันไม่ได้แก้ปัญหาความปลอดภัยของเธรดทั้งหมดอย่างน่าอัศจรรย์อย่างที่ชื่อบอกไว้

ฉันไม่ต้องการที่จะดูเหมือนว่าฉันกำลังเรียกร้องให้คุณไป "โอ้ฉันควรเริ่มเปิดเผยฟิลด์แทนคุณสมบัติ" ประเด็นก็คือถ้าคุณจำเป็นต้องใช้สมาชิกเหล่านี้เป็นประจำในการโทรที่ใช้พารามิเตอร์ "ref" หรือ "out" โดยเฉพาะอย่างยิ่งกับสิ่งที่อาจเป็นประเภทค่าแบบง่ายที่ไม่น่าจะต้องการองค์ประกอบที่มีมูลค่าเพิ่มใด ๆ อาร์กิวเมนต์สามารถทำได้


6

แม้ว่าเขตข้อมูลและคุณสมบัติจะมีลักษณะคล้ายกัน แต่มีองค์ประกอบทางภาษาที่แตกต่างกัน 2 อย่าง

  1. เขตข้อมูลเป็นกลไกเดียวที่จะเก็บข้อมูลในระดับชั้นเรียน ฟิลด์เป็นตัวแปรแนวคิดที่ขอบเขตของคลาส หากคุณต้องการจัดเก็บข้อมูลบางอย่างไปยังอินสแตนซ์ของคลาส (วัตถุ) ของคุณคุณต้องใช้ฟิลด์ ไม่มีทางเลือกอื่น คุณสมบัติไม่สามารถจัดเก็บข้อมูลใด ๆ แม้ว่ามันอาจดูเหมือนพวกเขาสามารถทำได้ เห็นเสียงร้อง

  2. ในทางกลับกันคุณสมบัติไม่เคยเก็บข้อมูล พวกเขาเป็นเพียงคู่ของวิธีการ (รับและตั้งค่า) ที่สามารถเรียก syntactically ในลักษณะที่คล้ายกันเป็นเขตข้อมูลและในกรณีส่วนใหญ่พวกเขาเข้าถึงเขตข้อมูล (สำหรับอ่านหรือเขียน) ซึ่งเป็นแหล่งที่มาของความสับสนบางอย่าง แต่เนื่องจากวิธีการคุณสมบัติ (ด้วยข้อ จำกัด บางอย่างเช่นต้นแบบถาวร) วิธีการทั่วไป C # พวกเขาสามารถทำสิ่งที่วิธีการปกติสามารถทำได้ มันหมายความว่าพวกเขาสามารถมี 1,000 บรรทัดของรหัสพวกเขาสามารถโยนข้อยกเว้นเรียกวิธีการอื่นได้แม้เสมือนนามธรรมหรือแทนที่ สิ่งที่ทำให้คุณสมบัติพิเศษคือความจริงที่ว่า C # คอมไพเลอร์เก็บเมตาดาต้าพิเศษบางอย่างลงในแอสเซมบลีที่สามารถใช้เพื่อค้นหาคุณสมบัติเฉพาะ - คุณลักษณะที่ใช้กันอย่างแพร่หลาย

รับและตั้งค่าวิธีการทรัพย์สินมีต้นแบบต่อไปนี้

PROPERTY_TYPE get();

void set(PROPERTY_TYPE value);

ดังนั้นหมายความว่าคุณสมบัติสามารถ 'จำลอง' โดยการกำหนดเขตข้อมูลและวิธีการที่สอดคล้องกัน 2 วิธี

class PropertyEmulation
{
    private string MSomeValue;

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

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

การจำลองคุณสมบัติดังกล่าวเป็นเรื่องปกติสำหรับภาษาการเขียนโปรแกรมที่ไม่สนับสนุนคุณสมบัติ - เช่น C ++ มาตรฐาน ใน C # นั้นคุณควรต้องการคุณสมบัติเป็นวิธีการเข้าถึงเขตข้อมูลของคุณ

เนื่องจากมีเพียงเขตข้อมูลเท่านั้นที่สามารถจัดเก็บข้อมูลได้หมายความว่ามีคลาสของเขตข้อมูลเพิ่มเติมมากขึ้นวัตถุหน่วยความจำของคลาสดังกล่าวจะหมด ในทางตรงกันข้ามการเพิ่มคุณสมบัติใหม่เข้าไปในคลาสไม่ได้ทำให้วัตถุของคลาสนั้นใหญ่ขึ้น นี่คือตัวอย่าง

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).

แม้ว่าวิธีการของคุณสมบัติสามารถทำสิ่งใดก็ได้ในกรณีส่วนใหญ่พวกเขาใช้เป็นวิธีการเข้าถึงเขตข้อมูลของวัตถุ หากคุณต้องการให้ฟิลด์อื่นสามารถเข้าถึงคลาสอื่นได้คุณสามารถทำได้ 2 วิธี

  1. ทำให้ฟิลด์เป็นสาธารณะ - ไม่แนะนำให้เลือก
  2. การใช้คุณสมบัติ

นี่คือคลาสที่ใช้ฟิลด์สาธารณะ

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;

ในขณะที่รหัสถูกต้องสมบูรณ์แบบจากมุมมองการออกแบบ แต่ก็มีข้อเสียหลายประการ เนื่องจากเขตข้อมูลสามารถเป็นทั้งอ่านและเขียนคุณไม่สามารถป้องกันผู้ใช้จากการเขียนไปยังเขตข้อมูล คุณสามารถใช้readonlyคำหลัก แต่ด้วยวิธีนี้คุณต้องเริ่มต้นฟิลด์แบบอ่านอย่างเดียวเท่านั้นในตัวสร้าง ยิ่งไปกว่านั้นไม่มีอะไรป้องกันไม่ให้คุณเก็บค่าที่ไม่ถูกต้องลงในช่องของคุณ

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

รหัสนั้นถูกต้องการมอบหมายทั้งหมดจะถูกดำเนินการแม้ว่าจะไร้เหตุผลก็ตาม Ageมีค่าลบYearOfBirthอยู่ไกลในอนาคตและไม่ตรงกับอายุและFullNameเป็นโมฆะ ด้วยฟิลด์ที่คุณไม่สามารถป้องกันผู้ใช้ในclass Nameการทำผิดพลาดดังกล่าว

นี่คือรหัสที่มีคุณสมบัติที่แก้ไขปัญหาเหล่านี้

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());
        }
    }
}

คลาสที่อัปเดตแล้วมีข้อดีดังต่อไปนี้

  1. FullNameและYearOfBirthมีการตรวจสอบค่าที่ไม่ถูกต้อง
  2. Ageไม่สามารถเขียนได้ มัน callculated จากYearOfBirthและปีปัจจุบัน
  3. คุณสมบัติใหม่FullNameInUppercaseแปลงFullNameเป็นกรณีบน นี่เป็นตัวอย่างเล็กน้อยของการใช้งานคุณสมบัติที่มีการใช้คุณสมบัติทั่วไปเพื่อแสดงค่าของฟิลด์ในรูปแบบที่เหมาะสมกับผู้ใช้มากขึ้น - ตัวอย่างเช่นการใช้ภาษาปัจจุบันกับตัวเลขเฉพาะของDateTimeรูปแบบ

ข้างนี้คุณสมบัติสามารถกำหนดเป็นเสมือนหรือแทนที่ - เพราะพวกเขาเป็นวิธีการ. NET ปกติ กฎเดียวกันนี้ใช้สำหรับวิธีการคุณสมบัติเช่นเดียวกับวิธีการปกติ

C # ยังสนับสนุนตัวสร้างดัชนีซึ่งเป็นคุณสมบัติที่มีพารามิเตอร์ดัชนีในวิธีการคุณสมบัติ นี่คือตัวอย่าง

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]);

เนื่องจาก C # 3.0 ช่วยให้คุณสามารถกำหนดคุณสมบัติอัตโนมัติ นี่คือตัวอย่าง

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

    public int Value2
    {
        get;
        set;
    }
}

แม้ว่าจะclass AutoPropsมีคุณสมบัติเท่านั้น (หรือดูเหมือนว่า) มันสามารถเก็บค่า 2 และขนาดของวัตถุของคลาสนี้เท่ากับsizeof(Value1)+sizeof(Value2)= 4 + 4 = 8 ไบต์

เหตุผลนี้ง่าย เมื่อคุณกำหนดคุณสมบัติอัตโนมัติคอมไพเลอร์ C # จะสร้างรหัสอัตโนมัติที่มีเขตข้อมูลที่ซ่อนอยู่และคุณสมบัติที่มีวิธีการเข้าถึงเขตข้อมูลที่ซ่อนอยู่นี้ นี่คือการรวบรวมรหัสผลิต

นี่คือรหัสที่ILSpyสร้างขึ้นจากการประกอบที่รวบรวม คลาสมีฟิลด์และคุณสมบัติที่ซ่อนอยู่ที่สร้างขึ้น

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;
        }
    }
}

ดังนั้นอย่างที่คุณเห็นคอมไพเลอร์ยังคงใช้เขตข้อมูลเพื่อเก็บค่า - เนื่องจากเขตข้อมูลเป็นวิธีเดียวที่จะเก็บค่าลงในวัตถุ

ดังที่คุณเห็นแม้ว่าคุณสมบัติและเขตข้อมูลจะมีไวยากรณ์การใช้งานที่คล้ายคลึงกัน แต่เป็นแนวคิดที่แตกต่างกันมาก แม้ว่าคุณจะใช้คุณสมบัติอัตโนมัติหรือเหตุการณ์ - เขตข้อมูลที่ซ่อนอยู่จะถูกสร้างขึ้นโดยคอมไพเลอร์ที่จัดเก็บข้อมูลจริง

หากคุณต้องการทำให้ค่าฟิลด์สามารถเข้าถึงได้จากโลกภายนอก (ผู้ใช้ในชั้นเรียนของคุณ) อย่าใช้ฟิลด์สาธารณะหรือเขตข้อมูลที่มีการป้องกัน ฟิลด์ควรถูกทำเครื่องหมายเป็นส่วนตัวเสมอ คุณสมบัติช่วยให้คุณสามารถตรวจสอบค่าการจัดรูปแบบการแปลง ฯลฯ และโดยทั่วไปจะทำให้รหัสของคุณปลอดภัยอ่านและขยายได้มากขึ้นสำหรับการแก้ไขในอนาคต


4

นอกจากนี้คุณสมบัติช่วยให้คุณใช้ตรรกะเมื่อตั้งค่า

ดังนั้นคุณสามารถบอกได้ว่าคุณต้องการตั้งค่าให้เป็นเขตข้อมูลจำนวนเต็มเท่านั้นหากค่ามากกว่า x มิฉะนั้นจะเกิดข้อยกเว้น

คุณสมบัติที่มีประโยชน์จริงๆ


4

หากคุณกำลังจะใช้เธรดพื้นฐานคุณจำเป็นต้องใช้ฟิลด์ คุณสมบัติสามารถทำลายรหัสเธรดของคุณ นอกเหนือจากนั้นสิ่งที่คอรีกล่าวถูกต้อง


1
ตั้งแต่เมื่อไหร่ ล็อกเขตข้อมูลสำรองของคุณภายในทรัพย์สินและเป็นสิ่งที่เท่าเทียมกัน
Sekhat

1
คุณสมบัติเป็นวิธีการและไม่ได้รับการพิจารณาโดย CIL JIT ใด ๆ ในวันนี้ หากคุณกำลังจะใช้เธรดแบบดั้งเดิมเช่น Interlocked คุณต้องมีฟิลด์ ตรวจสอบแหล่งที่มาของคุณ คำว่า 'ล็อค' เป็นคำที่ใช้ผิด
Jonathan C Dickinson

4

(นี่ควรเป็นความคิดเห็นจริงๆ แต่ฉันไม่สามารถโพสต์ความคิดเห็นได้ดังนั้นโปรดแก้ตัวถ้ามันไม่เหมาะสมกับโพสต์)

ฉันเคยทำงานในสถานที่ซึ่งแนวทางปฏิบัติที่แนะนำคือใช้เขตข้อมูลสาธารณะแทนคุณสมบัติเมื่อ def คุณสมบัติที่เทียบเท่าจะเพิ่งเข้าถึงเขตข้อมูลเช่นเดียวกับใน:

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

เหตุผลของพวกเขาคือฟิลด์สาธารณะสามารถเปลี่ยนเป็นทรัพย์สินได้ในภายหลังในอนาคตหากจำเป็น ในเวลานั้นฉันรู้สึกแปลกเล็กน้อย ตัดสินโดยโพสต์เหล่านี้ดูเหมือนว่าไม่มากที่นี่จะเห็นด้วย คุณอาจจะพูดอะไรเพื่อลองเปลี่ยนสิ่งต่างๆ

แก้ไข: ฉันควรเพิ่มว่ารหัสฐานทั้งหมด ณ ที่นี้ถูกรวบรวมในเวลาเดียวกันดังนั้นพวกเขาอาจคิดว่าการเปลี่ยนอินเทอร์เฟซสาธารณะของคลาส (โดยการเปลี่ยนเขตข้อมูลสาธารณะเป็นคุณสมบัติ) ไม่มีปัญหา


ตั้งแต่C # 3.0รูปแบบที่อธิบายไว้ที่นี่ได้รับการสนับสนุนอย่างสะดวกสบายโดยคุณสมบัติที่เรียกว่าคุณสมบัติที่ใช้งานอัตโนมัติ
DavidRR

ฉันคิดว่าข้อดีอย่างหนึ่งใน C # ที่มีคุณสมบัติเนื่องจากมี API เหมือนกับเขตข้อมูลดังนั้นลูกค้าของชั้นไม่สนใจว่าพวกเขากำลังเข้าถึงคุณสมบัติหรือเขตข้อมูล (นี่ไม่เป็นความจริงในตัวอย่าง C ++) .. ในการสร้างต้นแบบฉันคิดว่ามันมีเหตุผลที่จะเริ่มต้นด้วยเขตข้อมูลสาธารณะแล้วย้ายไปยังคุณสมบัติตามต้องการ มีประสิทธิภาพและหน่วยความจำที่เข้าชมพร้อมคุณสมบัติและมีการพิมพ์พิเศษ พวกเขาไม่ได้ฟรี แต่ถ้าคุณเปลี่ยนใจคุณไม่จำเป็นต้องปรับโครงสร้างโค้ดที่เกี่ยวข้อง
Mark Lakata

คุณสมบัติไม่สามารถใช้เป็นพารามิเตอร์ OUT หรือ REF ได้ดังนั้นการเปลี่ยนเขตข้อมูลลงในคุณสมบัติอาจทำให้เกิดข้อผิดพลาดในการรวบรวมบรรทัด หากค่าถูกนำมาใช้เป็นคุณสมบัติตั้งแต่เริ่มต้นมันจะไม่ถูกใช้เป็นพารามิเตอร์ OUT หรือ REF (VAR ใน Pascal / Delphi) และการเปลี่ยนแปลงใด ๆ ที่คุณทำใน getter / setter จะโปร่งใสต่อการใช้งาน
HeartWare

4

ในทางเทคนิคแล้วฉันไม่คิดว่ามีความแตกต่างเนื่องจากคุณสมบัติเป็นเพียงการล้อมรอบฟิลด์ที่สร้างโดยผู้ใช้หรือสร้างโดยคอมไพเลอร์โดยอัตโนมัติวัตถุประสงค์ของคุณสมบัติคือการบังคับใช้การห่อหุ้มและนำเสนอคุณสมบัติที่มีน้ำหนักเบา เป็นเพียงการปฏิบัติที่ไม่ดีในการประกาศฟิลด์ในฐานะสาธารณะ แต่ไม่มีปัญหาใด ๆ


4

ฟิลด์เป็นตัวแปรสมาชิกสามัญหรืออินสแตนซ์สมาชิกของคลาส คุณสมบัติเป็นนามธรรมที่จะได้รับและการตั้งค่าของพวกเขา คุณสมบัติจะเรียกว่า accessors ด้วยเนื่องจากพวกเขาเสนอวิธีการเปลี่ยนและดึงข้อมูลในฟิลด์ถ้าคุณเปิดเผยฟิลด์ในคลาสเป็นแบบส่วนตัว โดยทั่วไปคุณควรประกาศตัวแปรสมาชิกของคุณเป็นส่วนตัวจากนั้นประกาศหรือกำหนดคุณสมบัติสำหรับพวกเขา

  class SomeClass
  {
     int numbera; //Field

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

  }

3

คุณสมบัติห่อหุ้มฟิลด์ซึ่งช่วยให้คุณสามารถดำเนินการประมวลผลเพิ่มเติมเกี่ยวกับค่าที่จะตั้งหรือดึงข้อมูล โดยทั่วไปแล้วจะใช้งานเกินขนาดหากคุณจะไม่ทำอะไรก่อนหรือหลังการประมวลผลบนค่าของฟิลด์


1
ไม่ฉันใช้คุณสมบัติทุกครั้งมันช่วยให้คุณมีความยืดหยุ่นในการเปลี่ยนการใช้งานได้ตลอดเวลาโดยไม่ทำลาย API ของคุณ
Sekhat

เกี่ยวกับวิวัฒนาการ API คุณสามารถใช้ฟิลด์สำหรับข้อมูลส่วนตัวโดยไม่มีปัญหา นอกจากนี้ในกรณีแปลก ๆ ที่คุณต้องการแบ่งปันข้อมูลภายในแอสเซมบลีคุณสามารถให้การเข้าถึง 'ภายใน' ของฟิลด์
Daniel Earwicker

3

IMO, คุณสมบัติเป็นเพียง "SetXXX ()" "GetXXX ()" ฟังก์ชั่น / วิธีการ / การเชื่อมต่อคู่ที่เราใช้ก่อนหน้านี้ แต่พวกเขาจะกระชับและสง่างามมากขึ้น


3

การตั้งค่าฟิลด์ส่วนตัวตามธรรมเนียมผ่าน getter และ setter เพื่อประโยชน์ของรหัสน้อยคุณสามารถใช้คุณสมบัติในการตั้งค่าเขตข้อมูลแทน


3

เมื่อคุณมีคลาสซึ่งก็คือ "รถยนต์" คุณสมบัติเป็นสีรูปร่าง ..

โดยที่ฟิลด์เป็นตัวแปรที่กำหนดไว้ภายในขอบเขตของคลาส


3

จาก Wikipedia - โปรแกรมเชิงวัตถุ :

การเขียนโปรแกรมเชิงวัตถุ (OOP) เป็นกระบวนทัศน์การเขียนโปรแกรมตามแนวคิดของ "วัตถุ" ซึ่งเป็นโครงสร้างข้อมูลที่มีข้อมูลในรูปแบบของเขตข้อมูลมักจะเรียกว่าคุณลักษณะ; และรหัสในรูปแบบของวิธีการที่รู้จักกันมักจะเป็นวิธีการ (เน้นเพิ่ม)

คุณสมบัติเป็นส่วนหนึ่งของพฤติกรรมของวัตถุ แต่ถูกออกแบบมาเพื่อให้ผู้ใช้ของวัตถุมีภาพลวงตา / นามธรรมของการทำงานกับข้อมูลของวัตถุ


3

การออกแบบฟิลด์ของฉันคือฟิลด์ต้องแก้ไขโดยผู้ปกครองเท่านั้นดังนั้นคลาส ส่งผลให้ตัวแปรกลายเป็นแบบส่วนตัวจากนั้นจะสามารถให้สิทธิ์ในการอ่านคลาส / วิธีการด้านนอกฉันผ่านระบบทรัพย์สินที่มีเพียงรับ เขตข้อมูลจะถูกเรียกคืนโดยคุณสมบัติและอ่านอย่างเดียว! ถ้าคุณต้องการแก้ไขมันคุณจะต้องผ่านวิธีการ (ตัวอย่างเช่น Constructor) และฉันพบว่าด้วยวิธีนี้ที่ทำให้คุณปลอดภัยเราสามารถควบคุมรหัสของเราได้ดีขึ้นเพราะเรา "แปลน" เราสามารถใส่ทุกอย่างในที่สาธารณะได้เสมอดังนั้นทุกกรณีที่เป็นไปได้ความคิดเกี่ยวกับตัวแปร / วิธีการ / ชั้นเรียน ฯลฯ ... ในความคิดของฉันเป็นเพียงความช่วยเหลือในการพัฒนาการบำรุงรักษาโค้ด ตัวอย่างเช่นหากบุคคลที่ดำเนินการต่อรหัสด้วยเขตข้อมูลสาธารณะเขาสามารถทำอะไรและดังนั้นสิ่งที่ "ไร้เหตุผล" ในส่วนที่เกี่ยวกับวัตถุประสงค์ตรรกะของสาเหตุที่รหัสถูกเขียนขึ้น มันเป็นมุมมองของฉัน

เมื่อฉันใช้คลาสสิกคลาสสิกฟิลด์ส่วนตัว / สาธารณะอ่านได้อย่างเดียวสำหรับ 10 สาขาเอกชนฉันควรเขียน 10 คุณสมบัติสาธารณะ! รหัสสามารถใหญ่ขึ้นเร็วขึ้นจริงๆ ฉันค้นพบตัวตั้งค่าส่วนตัวและตอนนี้ฉันใช้คุณสมบัติสาธารณะกับตัวตั้งค่าส่วนตัวเท่านั้น ตัวตั้งค่าสร้างในพื้นหลังเขตข้อมูลส่วนตัว

นั่นเป็นเหตุผลว่าทำไมรูปแบบการเขียนโปรแกรมคลาสสิคแบบเก่าของฉันคือ:

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

รูปแบบการเขียนโปรแกรมใหม่ของฉัน:

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

ใช่ฉันไม่ดีขอโทษ!
Tony Pinot

3

ลองคิดดูสิ: คุณมีห้องและประตูที่จะเข้าไปในห้องนี้ หากคุณต้องการตรวจสอบว่าใครเข้ามาและรักษาความปลอดภัยในห้องของคุณคุณควรใช้คุณสมบัติไม่เช่นนั้นจะไม่มีประตูใด ๆ และทุกคนสามารถเข้ามาได้อย่างง่ายดายโดยไม่มีข้อบังคับใด ๆ

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

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

ผู้คนเข้ามาในส่วนหนึ่งได้อย่างง่ายดายไม่มีการตรวจสอบใด ๆ

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";

ตอนนี้คุณตรวจสอบบุคคลและรู้ว่าเขามีสิ่งชั่วร้ายกับเขาหรือไม่


3

ฟิลด์เป็นตัวแปรในคลาส เขตข้อมูลเป็นข้อมูลที่คุณสามารถห่อหุ้มผ่านการใช้งานของการปรับเปลี่ยนการเข้าถึง

คุณสมบัติคล้ายกับฟิลด์ที่กำหนดสถานะและข้อมูลที่เกี่ยวข้องกับวัตถุ

ซึ่งแตกต่างจากสนามคุณสมบัติมีไวยากรณ์พิเศษที่ควบคุมวิธีการที่คนอ่านข้อมูลและเขียนข้อมูลเหล่านี้จะเรียกว่าผู้ประกอบการได้รับและการตั้งค่า ตรรกะการตั้งค่ามักจะสามารถใช้ในการตรวจสอบ


2

คุณสมบัติเป็นสมาชิกชั้นเรียนชนิดพิเศษในคุณสมบัติเราใช้ชุด Set หรือ Get ที่กำหนดไว้ล่วงหน้าโดยใช้ accessors ซึ่งเราสามารถอ่านเขียนหรือเปลี่ยนค่าของฟิลด์ส่วนตัว

ตัวอย่างเช่นให้เราใช้คลาสที่Employeeมีฟิลด์ส่วนตัวสำหรับชื่ออายุและ Employee_Id เราไม่สามารถเข้าถึงฟิลด์เหล่านี้จากนอกคลาส แต่เราสามารถเข้าถึงฟิลด์ส่วนตัวเหล่านี้ผ่านคุณสมบัติ

ทำไมเราถึงใช้คุณสมบัติ?

ทำให้ฟิลด์คลาสเป็นแบบสาธารณะและเปิดเผยว่ามีความเสี่ยงเนื่องจากคุณจะไม่สามารถควบคุมสิ่งที่ได้รับมอบหมาย & ส่งคืนได้

เพื่อให้เข้าใจอย่างชัดเจนด้วยตัวอย่างให้นักเรียนชั้นเรียนที่มี ID, รหัสผ่าน, ชื่อ ในตัวอย่างนี้มีปัญหากับฟิลด์สาธารณะ

  • ID ไม่ควรเป็น -ve
  • ไม่สามารถตั้งชื่อเป็น null ได้
  • ควรอ่านเครื่องหมายผ่านเท่านั้น
  • หากไม่มีชื่อนักเรียนควรส่งคืนชื่อ

ในการลบปัญหานี้เราใช้วิธีรับและตั้งค่า

// 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
    }
}

ตอนนี้เรานำตัวอย่างของวิธี get และ 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

ข้อมูลเพิ่มเติม: โดยค่าเริ่มต้นการเข้าถึงและการตั้งค่า accessors จะสามารถเข้าถึงได้เหมือนกับทรัพย์สิน คุณสามารถควบคุม / จำกัด การเข้าถึงสำหรับแต่ละคน (เพื่อรับและตั้งค่า) โดยใช้ตัวดัดแปลงการเข้าถึงที่ จำกัด มากขึ้น

ตัวอย่าง:

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

รับที่นี่ยังคงเข้าถึงสาธารณะ (เป็นทรัพย์สินที่เป็นสาธารณะ) แต่การตั้งค่าได้รับการคุ้มครอง


2

คุณสมบัติที่ใช้ในการเปิดเผยข้อมูล พวกเขาใช้ accessors (set, get) ซึ่งค่าของฟิลด์ส่วนตัวสามารถอ่านเขียนหรือจัดการได้

คุณสมบัติไม่ได้ตั้งชื่อที่เก็บสินค้า แต่พวกเขามี accessors ที่อ่านเขียนหรือคำนวณค่าของพวกเขา

การใช้คุณสมบัติเราสามารถตั้งค่าการตรวจสอบกับประเภทของข้อมูลที่ตั้งค่าในเขตข้อมูล

ตัวอย่างเช่นเรามีอายุฟิลด์จำนวนเต็มส่วนตัวว่าเราควรอนุญาตค่าบวกเนื่องจากอายุไม่สามารถลบได้

เราสามารถทำได้สองวิธีโดยใช้ตัวรับและตัวตั้งค่าและการใช้คุณสมบัติ

 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()
       }
    }

คุณสมบัติใช้งานอัตโนมัติหากเราไม่ใช้ตรรกะในการรับและตั้งค่า accessors เราสามารถใช้คุณสมบัติใช้งานอัตโนมัติ

เมื่อคอมไพล์คุณสมบัติที่ดำเนินการโดยอัตโนมัติสร้างฟิลด์ส่วนตัวที่ไม่ระบุชื่อที่สามารถเข้าถึงได้ผ่านการรับและตั้งค่า accessors

public int Age{get;set;}

คุณสมบัติ นามธรรมคลาสนามธรรมอาจมีคุณสมบัตินามธรรมซึ่งควรนำไปใช้ในคลาสที่ได้รับ

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;
     }
  }

เราสามารถตั้งค่าทรัพย์สินส่วนตัวในที่นี้เราสามารถตั้งค่าคุณสมบัติอัตโนมัติแบบส่วนตัว (ตั้งค่าด้วยในชั้นเรียน)

public int MyProperty
{
    get; private set;
}

คุณสามารถบรรลุเดียวกันกับรหัสนี้ ในคุณสมบัติชุดคุณสมบัตินี้ไม่สามารถใช้งานได้เนื่องจากเราต้องตั้งค่าเป็นฟิลด์โดยตรง

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

2

กรณีส่วนใหญ่นั้นจะเป็นชื่อคุณสมบัติที่คุณเข้าถึงซึ่งแตกต่างจากชื่อตัวแปร ( ฟิลด์ ) เหตุผลที่ถือว่าเป็นแนวปฏิบัติที่ดีใน. NET และใน C # โดยเฉพาะเพื่อปกป้องข้อมูลทุกชิ้นภายในชั้นเรียน ไม่ว่าจะเป็นตัวแปรอินสแตนซ์หรือตัวแปรสแตติก (ตัวแปรคลาส) เนื่องจากเกี่ยวข้องกับคลาส

ปกป้องตัวแปรเหล่านั้นทั้งหมดด้วยคุณสมบัติที่สอดคล้องกันซึ่งช่วยให้คุณสามารถกำหนดตั้งค่าและรับ อุปกรณ์เสริมและทำสิ่งต่าง ๆ เช่นการตรวจสอบความถูกต้องเมื่อคุณจัดการกับข้อมูลเหล่านั้น

แต่ในกรณีอื่น ๆ เช่นMath class (System namespace) มีคุณสมบัติสแตติกสองสามตัวที่สร้างขึ้นในคลาส หนึ่งในนั้นคือค่าคงที่ทางคณิตศาสตร์ของPI

เช่น. Math.PI

และเนื่องจาก PI เป็นข้อมูลที่กำหนดชัดเจนเราจึงไม่จำเป็นต้องมีหลายสำเนาของ PI จึงเป็นค่าเดียวกันเสมอ ดังนั้นบางครั้งตัวแปรสแตติกจะถูกใช้เพื่อแชร์ข้อมูลระหว่างวัตถุของคลาส แต่ยังใช้สำหรับข้อมูลคงที่โดยทั่วไปซึ่งคุณต้องการเพียงหนึ่งสำเนาของข้อมูล

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.