แนวทางปฏิบัติที่ดีที่สุดของ Getters, setters และ Properties Java กับ C #


92

ตอนนี้ฉันกำลังเรียน C # และฉันกำลังพยายามหาวิธีที่ดีที่สุดในการทำสิ่งต่างๆ ฉันมาจากพื้นหลัง Java ดังนั้นฉันจึงคุ้นเคยกับแนวทางปฏิบัติที่ดีที่สุดของ Java เท่านั้น ฉันเป็นมือใหม่ C #!

ใน Java ถ้าฉันมีทรัพย์สินส่วนตัวฉันทำสิ่งนี้

private String name;

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

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

ใน C # ฉันเห็นว่ามีหลายวิธีในการทำเช่นนี้

ฉันทำได้เหมือน Java:

private string name;

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

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

หรือฉันสามารถทำได้ด้วยวิธีนี้:

private string name;

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

หรือ:

public string Name { get; set; }

ฉันควรใช้ข้อใดและข้อควรระวังหรือรายละเอียดปลีกย่อยที่เกี่ยวข้องกับแต่ละแนวทางคืออะไร เมื่อสร้างคลาสฉันกำลังทำตามแนวทางปฏิบัติทั่วไปที่ดีที่สุดที่ฉันรู้จักจาก Java (โดยเฉพาะการอ่าน Effective Java) ตัวอย่างเช่นฉันชอบการไม่เปลี่ยนรูป (ให้ตัวตั้งค่าเมื่อจำเป็นเท่านั้น) ฉันแค่อยากรู้ว่าแนวทางปฏิบัติเหล่านี้เข้ากับวิธีการต่างๆในการจัดหา setters และ getters ใน C # ได้อย่างไร โดยพื้นฐานแล้วฉันจะแปลแนวทางปฏิบัติที่ดีที่สุดจากโลก Java เป็น C # ได้อย่างไร

แก้ไข

ฉันโพสต์สิ่งนี้เป็นความคิดเห็นสำหรับคำตอบของ Jon Skeet แต่มันก็ยาว:

สิ่งที่เกี่ยวกับคุณสมบัติที่ไม่สำคัญ (เช่นอาจมีการประมวลผลและการตรวจสอบที่สำคัญ)? ฉันยังสามารถเปิดเผยผ่านทรัพย์สินสาธารณะได้ แต่ด้วยตรรกะที่ห่อหุ้มgetและset? เหตุใดฉันจึง / ควรทำเช่นนี้โดยมีเมธอด setter และ getter เฉพาะ (พร้อมด้วยตรรกะการประมวลผลและการตรวจสอบที่เกี่ยวข้อง)

คำตอบ:


88

Pre-C # 6

ฉันจะใช้สิ่งสุดท้ายเหล่านี้สำหรับทรัพย์สินเล็กน้อย โปรดทราบว่าฉันจะเรียกสิ่งนี้ว่าสาธารณะทรัพย์สินเนื่องจากทั้ง getters และ setters เป็นสาธารณะ

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

public string Foo { get; private set; }

ซึ่งไม่เปลี่ยนรูปจริง ๆ ... อยู่นอกชั้นเรียนของคุณไม่เปลี่ยนรูป ดังนั้นคุณอาจต้องการใช้คุณสมบัติอ่านอย่างเดียวจริงแทน:

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

แน่นอนคุณไม่ต้องการที่จะเขียนและgetName() setName()ในบางกรณีคุณควรเขียนวิธีการรับ / ตั้งค่าแทนที่จะใช้คุณสมบัติโดยเฉพาะอย่างยิ่งหากอาจมีราคาแพงและคุณต้องการเน้นว่า อย่างไรก็ตามคุณต้องการทำตามหลักการตั้งชื่อ. NET ของ PascalCase สำหรับวิธีการและคุณไม่ต้องการให้คุณสมบัติที่ไม่สำคัญเช่นนี้ถูกนำไปใช้ด้วยวิธีการปกติอีกต่อไป - คุณสมบัติเป็นสำนวนมากกว่าที่นี่

ค # 6

ไชโยในที่สุดเราก็มีคุณสมบัติที่เหมาะสมสำหรับการอ่านอย่างเดียวโดยอัตโนมัติ:

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

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

public double Area => height * width;

5
ที่แน่นอนมากขึ้น: การอ่านโค้ดใด ๆ จะชี้ให้เห็นว่าทาง java คือแฮ็คที่ข้ามการสร้างรันไทม์ภาษา AND (!) ที่ถูกต้องและฆ่าการใช้คุณสมบัติเป็น proeprty (เช่น object.property = "value") ในบางทีมสิ่งนี้จะส่งผลให้เกิดการพูดคุยเกี่ยวกับทัศนคติที่ดี - ขึ้นอยู่กับความอาวุโสรวมกับแรงจูงใจในการนำทัศนคตินั้นไปใช้กับคู่แข่ง อย่างจริงจังอย่าต่อสู้กับภาษา โดยเฉพาะอย่างยิ่งในขณะที่ java "way" เป็นแฮ็กที่ถูกเลือกเพื่อไม่ให้แก้ไขภาษาสำหรับการสนับสนุนคุณสมบัติจริง
TomTom

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

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

2
@rtindru: ใช่ฉันรู้เรื่องนั้น เป็นไปได้ทั้งหมดที่จะเขียนคุณสมบัติแบบอ่านอย่างเดียว แต่คุณไม่สามารถประกาศคุณสมบัติที่ใช้งานโดยอัตโนมัติได้หากไม่มีตัวเข้าถึงชุด
Jon Skeet


17

หากสิ่งที่คุณต้องการคือตัวแปรในการจัดเก็บข้อมูล:

public string Name { get; set; }

ต้องการทำให้เป็นแบบอ่านอย่างเดียวหรือไม่?

public string Name { get; private set; }

หรือดียิ่งขึ้น ...

private readonly string _name;

...

public string Name { get { return _name; } }

ต้องการตรวจสอบมูลค่าก่อนกำหนดคุณสมบัติหรือไม่?

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

      m_name = value;
   }
}

โดยทั่วไป GetXyz () และ SetXyz () จะใช้ในบางกรณีเท่านั้นและคุณต้องใช้ความรู้สึกเมื่อรู้สึกถูกต้อง โดยทั่วไปแล้วฉันจะบอกว่าฉันคาดหวังว่าคุณสมบัติ get / set ส่วนใหญ่จะไม่มีตรรกะมากนักและมีผลข้างเคียงที่ไม่คาดคิดน้อยมาก (ถ้ามี) ถ้าอ่านค่าคุณสมบัติต้องเรียกใช้บริการหรือได้รับข้อมูลจากผู้ใช้ในการสั่งซื้อที่จะสร้างวัตถุที่ฉันขอแล้วฉันจะตัดมันลงไปในวิธีการและเรียกว่าสิ่งที่ต้องการมากกว่าBuildXyz()GetXyz()


2
ฉันจะไม่โยนข้อยกเว้นในคุณสมบัติฉันควรใช้วิธี setters กับสัญญาเพื่อระบุพฤติกรรมเฉพาะดังกล่าว หากคุณสมบัติเป็นประเภท int ฉันคาดหวังว่าทุก int จะมีคุณสมบัติ สิ่งที่เรียกร้องให้มีการโยนข้อยกเว้นนั้นไม่ใช่เรื่องง่ายในความคิดของฉันการเรียกใช้ประเภท INotifyPropertyChanged มีมากกว่าในบรรทัดนั้นตามฉัน
flindeberg

3
setter ส่วนตัว! = ไม่เปลี่ยนรูป
piedar

piedar พูดถูก ตัวตั้งค่าส่วนตัวหมายความว่าฉันไม่สามารถทำการมอบหมายได้ แต่ฉันยังสามารถmyList.Add()ใช้ได้ตัวอย่างเช่น (ตราบใดที่วัตถุมีการเปลี่ยนแปลงมันไม่แน่นอน)

1
@flindeberg ขออภัย แต่ฉันไม่เห็นด้วย ... ถ้าคุณมีแถบความคืบหน้าพร้อม a Min/ Maxvalue คุณต้องการให้แน่ใจว่าMax > Min? การมีSetRange(Min, Max)อาจจะสมเหตุสมผล แต่คุณจะอ่านค่าเหล่านั้นกลับคืนมาได้อย่างไร? อ่านอย่างเดียวคุณสมบัติต่ำสุด / สูงสุด? การโยนข้อยกเว้นสำหรับการป้อนข้อมูลที่ไม่ถูกต้องดูเหมือนจะเป็นวิธีที่สะอาดที่สุดในการจัดการสิ่งนี้
พื้นฐาน

12

ใช้คุณสมบัติใน C # ไม่ใช่วิธีการรับ / ตั้งค่า พวกเขาอยู่ที่นั่นเพื่อความสะดวกของคุณและเป็นสำนวน

สำหรับตัวอย่าง C # สองตัวอย่างของคุณหนึ่งเป็นเพียงแค่น้ำตาลซินแทติกสำหรับอีกตัวอย่างหนึ่ง ใช้คุณสมบัติอัตโนมัติหากสิ่งที่คุณต้องการคือ Wrapper อย่างง่ายรอบ ๆ ตัวแปรอินสแตนซ์ใช้เวอร์ชันเต็มเมื่อคุณต้องการเพิ่มตรรกะใน getter และ / หรือ setter


5

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

ฉันชอบคุณสมบัติอัตโนมัติเมื่อเป็นไปได้ แต่คุณไม่ควรทำคู่ set / get ใน C #


5
public string Name { get; set; }

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

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

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


4

ไม่ว่าคุณจะเลือกทางใดใน C # ผลลัพธ์ก็เหมือนกัน คุณจะได้รับตัวแปร backinng พร้อมวิธี getter และ setter แยกกัน การใช้คุณสมบัติคุณกำลังปฏิบัติตามแนวทางปฏิบัติที่ดีที่สุดดังนั้นจึงเป็นเรื่องสำคัญว่าคุณต้องการได้รับรายละเอียดอย่างไร

โดยส่วนตัวแล้วฉันจะเลือกคุณสมบัติอัตโนมัติเวอร์ชันสุดท้าย: public string Name { get; set; }เนื่องจากใช้พื้นที่น้อยที่สุด และคุณสามารถขยายสิ่งเหล่านี้ได้ในอนาคตหากคุณต้องการเพิ่มบางอย่างเช่นการตรวจสอบความถูกต้อง


4

เมื่อใดก็ตามที่เป็นไปได้ฉันชอบสาธารณะstring Name { get; set; }เพราะมันสั้นและอ่านได้ง่าย อย่างไรก็ตามอาจมีบางครั้งที่จำเป็น

private string name;

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

2
คุณช่วยอธิบายได้ไหมว่าเมื่อไหร่และทำไมถึงจำเป็น?
Jesper

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

3
แล้วทำไมถึงเป็น 'lolz'? คุณสามารถแสดงการสนับสนุนของคุณสำหรับความคิดเห็นโดยการโหวต
SquidScareMe

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

4

ใน C # วิธีที่ต้องการคือผ่านคุณสมบัติมากกว่าgetX()และsetX()วิธีการ นอกจากนี้โปรดทราบว่า C # ไม่ต้องการให้คุณสมบัติมีทั้ง get และ set - คุณสามารถมีคุณสมบัติ get-only และ set-only properties

public boolean MyProperty
{
    get { return something; }
}

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

4

ก่อนอื่นให้ฉันพยายามอธิบายสิ่งที่คุณเขียน:

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

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

ด้วย. 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
}

ขอให้คุณโชคดีใน C #


3

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


2

เช่นเดียวกับคำตอบส่วนใหญ่ที่นี่ให้ใช้คุณสมบัติอัตโนมัติ ใช้งานง่ายโค้ดน้อยและสะอาดมากขึ้น หากคุณควรจัดลำดับชั้นเรียนของคุณให้ทำเครื่องหมายที่คลาส[Serializable]/ ด้วย[DataConract]แอตทริบิวต์ และหากคุณใช้[DataContract]เครื่องหมายสมาชิกด้วย

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

ตัวตั้งค่าส่วนตัวหรือสาธารณะขึ้นอยู่กับความต้องการของคุณ

โปรดทราบว่าคุณสมบัติอัตโนมัติต้องการทั้ง getters และ setters (public หรือ private)

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

หรือหากคุณต้องการแค่รับ / ตั้งค่าให้สร้างฟิลด์สำรองด้วยตัวคุณเอง


0

ฉันควรใช้ข้อใดและข้อควรระวังหรือรายละเอียดปลีกย่อยที่เกี่ยวข้องกับแต่ละแนวทางคืออะไร

เมื่อไปกับคุณสมบัติมีข้อแม้อย่างหนึ่งที่ยังไม่ได้กล่าวถึง: ด้วยคุณสมบัติคุณไม่สามารถมีพาราเมตไตรเซชันของ getters หรือ setters ของคุณได้

ตัวอย่างเช่นสมมติว่าคุณต้องการเรียกข้อมูลรายการและต้องการใช้ตัวกรองในเวลาเดียวกัน ด้วย get-method คุณสามารถเขียนสิ่งที่ต้องการ:

obj.getItems(filter);

ในทางตรงกันข้ามกับทรัพย์สินคุณจะถูกบังคับให้ส่งคืนสินค้าทั้งหมดก่อน

obj.items

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

obj.itemsFilteredByX
obj.itemsFilteredByY

สิ่งที่บางครั้งอาจสร้างความรำคาญก็คือเมื่อคุณเริ่มต้นด้วยคุณสมบัติเช่นobj.itemsแล้วค้นพบในภายหลังว่าจำเป็นต้องใช้ getter- หรือ setter-parametrization หรือจะทำให้สิ่งต่างๆง่ายขึ้นสำหรับผู้ใช้ class-API ตอนนี้คุณจำเป็นต้องเขียน API ของคุณใหม่และแก้ไขตำแหน่งเหล่านั้นทั้งหมดในโค้ดของคุณที่เข้าถึงคุณสมบัตินี้หรือค้นหาทางเลือกอื่น ในทางตรงกันข้ามกับ get-method เช่นobj.getItems()คุณสามารถขยายลายเซ็นของเมธอดของคุณเพื่อยอมรับอ็อบเจ็กต์ "configuration" ที่เป็นทางเลือกเช่นobj.getItems(options)โดยไม่ต้องเขียนซ้ำตำแหน่งที่เรียกเมธอดของคุณ

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

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