คุณสมบัติอัตโนมัติ C # 3.0 - มีประโยชน์หรือไม่? [ปิด]


155

หมายเหตุ: นี่ถูกโพสต์เมื่อฉันเริ่มต้น C # ด้วยความรู้ในปี 2014 ฉันสามารถพูดได้อย่างแท้จริงว่าคุณสมบัติรถยนต์เป็นหนึ่งในสิ่งที่ดีที่สุดที่เคยเกิดขึ้นกับภาษา C #

ฉันใช้เพื่อสร้างคุณสมบัติของฉันใน C # โดยใช้ฟิลด์ส่วนตัวและฟิลด์สาธารณะ:

private string title;
public string Title
{
    get { return title;  }
    set { title = value;  }
}

ตอนนี้ด้วย. NET 3.0 เราได้รับคุณสมบัติอัตโนมัติ:

public string Title { get; set; }

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

ในความเป็นจริงเขตข้อมูลส่วนตัวที่ซ่อนไม่แม้แต่จะปรากฏในดีบักเกอร์ซึ่งก็โอเคเนื่องจากข้อเท็จจริงที่ว่าฟังก์ชั่น get / set ไม่ทำอะไรเลย แต่เมื่อฉันต้องการใช้ตรรกะ getter / setter จริงฉันต้องใช้คู่ส่วนตัว / สาธารณะต่อไป

ฉันเห็นประโยชน์ที่ฉันบันทึกรหัสจำนวนมาก (หนึ่งต่อหกบรรทัด) โดยไม่สูญเสียความสามารถในการเปลี่ยนตรรกะตัวรับ / ตัวตั้งค่าในภายหลัง แต่จากนั้นอีกครั้งฉันสามารถทำเช่นนั้นได้โดยเพียงแค่ประกาศฟิลด์สาธารณะ ความต้องการของ {รับ; ตั้ง; } block ดังนั้นจึงประหยัดรหัสได้อีก

ดังนั้นสิ่งที่ฉันหายไปที่นี่? ทำไมทุกคนถึงต้องการใช้คุณสมบัติอัตโนมัติ


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

คำตอบ:


118

เราใช้พวกมันตลอดเวลาใน Stack Overflow

นอกจากนี้คุณยังอาจสนใจในการอภิปรายของคุณสมบัติเทียบกับตัวแปรสาธารณะ IMHO นั่นคือสิ่งที่สิ่งนี้มีปฏิกิริยาต่อและเพื่อจุดประสงค์นั้นมันยอดเยี่ยม


62

ใช่มันแค่บันทึกรหัส มันง่ายกว่าที่จะอ่านเมื่อคุณมีจำนวนมาก เขียนได้เร็วกว่าและบำรุงรักษาง่ายกว่า การบันทึกรหัสเป็นเป้าหมายที่ดีเสมอ

คุณสามารถตั้งค่าขอบเขตที่แตกต่างกัน:

public string PropertyName { get; private set; }

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

ในฐานะของ C # 6 คุณยังสามารถสร้างreadonlyคุณสมบัติจริง- เช่นคุณสมบัติที่ไม่เปลี่ยนรูปซึ่งไม่สามารถเปลี่ยนแปลงได้นอกตัวสร้าง:

public string PropertyName { get; }

public MyClass() { this.PropertyName = "whatever"; }

ในเวลารวบรวมที่จะกลายเป็น:

readonly string pName;
public string PropertyName { get { return this.pName; } }

public MyClass() { this.pName = "whatever"; }

ในชั้นเรียนที่ไม่เปลี่ยนรูปพร้อมกับสมาชิกจำนวนมากสิ่งนี้จะบันทึกรหัสที่มากเกินไป


"ดังนั้นคุณจะไม่สูญเสียฟังก์ชันการทำงานใด ๆ " คุณจะแก้ไขข้อบกพร่องได้อย่างไร
wal

2
@wal - มีการแก้ปัญหาอะไร จากมุมมองนั้นคุณจะต้องจัดการกับตัวแปรสมาชิก
Keith

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

3
เราอาจต้องใช้สิ่งนี้นอกคี ธ :)
wal

1
แต่ตกลงสมมติว่าคุณมีการตั้งค่าการโทรไปยัง myObj.Title จำนวนมาก ... คุณต้องการดูว่าค่าเปลี่ยนจาก "text" เป็น null หรือไม่นั่นคือจุดพักแบบมีเงื่อนไข คุณประสบความสำเร็จได้อย่างไร คุณไม่สามารถตั้งเบรกพอยต์บน setter ได้
wal

45

ข้อเสียใหญ่สามประการในการใช้ฟิลด์แทนคุณสมบัติคือ:

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

7
"ถ้าคุณเริ่มใช้ฟิลด์คุณจะไม่สามารถเปลี่ยนเป็นคุณสมบัติได้ในภายหลัง (ง่าย)" ขออภัย แต่ทำไม
Homam

6
@Homam ส่วนใหญ่รหัสผู้ใช้ที่ใช้การสะท้อนในฟิลด์ของคุณจะแตกเนื่องจากพวกเขาจะต้องเปลี่ยนจากการใช้ FieldInfo เป็น PropertyInfo
WCWedin

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

1
การคอมไพล์ซ้ำและสะท้อนกลับกันมันเป็นเรื่องง่ายมากที่จะห่อหุ้มฟิลด์โดยใช้ Visual Studio: Ctrl-R + E จะช่วยให้คุณสามารถเปลี่ยนฟิลด์เป็นคุณสมบัติที่มีตัวรับ / setters ที่เหมาะสม (หรือคลิกขวาที่ฟิลด์อีกครั้งคำนึงถึงฟิลด์แค็ปซูล)
JoeBrockhaus

@ เขต Hommam เป็นค่า lvalues ​​(เป็นตัวแปร) และคุณสมบัติไม่ใช่ สิ่งที่อาจมีการรวบรวมเมื่อเป็นเขตข้อมูลอาจไม่ได้เมื่อเป็นสถานที่ให้บริการ
Mark

29

โดยส่วนตัวแล้วฉันชอบคุณสมบัติอัตโนมัติ เกิดอะไรขึ้นกับการบันทึกบรรทัดของรหัส หากคุณต้องการทำสิ่งต่างๆใน getters หรือ setters จะไม่มีปัญหาในการแปลงเป็นคุณสมบัติปกติในภายหลัง

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

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

ฉันเดาว่ามันเหมือนกับคำหลัก var เรื่องของความชอบส่วนตัว


29

จาก Bjarne Stroustrup ผู้สร้าง C ++:

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

และคุณรู้อะไรไหม เขาพูดถูก. บ่อยแค่คุณห่อเขตข้อมูลส่วนตัวในการรับและการตั้งค่าโดยไม่ต้องทำอะไรจริง ๆ ภายในการรับ / การตั้งค่าเพียงเพราะมันเป็น "วัตถุเชิง" สิ่งที่ต้องทำ นี่คือวิธีการแก้ปัญหาของ Microsoft; โดยทั่วไปเป็นเขตข้อมูลสาธารณะที่คุณสามารถผูกกับ


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

18

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

private readonly string title;
public string Title
{
    get { return this.title; }
}

(โดยที่ฟิลด์นั้นเริ่มต้นได้ในตัวสร้างผ่านพารามิเตอร์ที่ผ่านและจากนั้นอ่านได้อย่างเดียว)

ดังนั้นสิ่งนี้จึงมีข้อได้เปรียบมากกว่าแบบธรรมดาget/ private setอัตโนมัติ


หากคุณมีการเปลี่ยนแปลงคุณสมบัติใด ๆ มันส่งผลให้โครงสร้างใหม่ นี่จะเป็นปัญหาถ้าคุณต้องการประเภทการอ้างอิงที่ไม่เปลี่ยนรูปภายใน - ฉันไม่เห็นเหตุผลที่คุณจะต้องใช้
Keith

@ Keith: ประโยคแรกของคุณดูเหมือนจะไม่ถูกต้องตามข้อเท็จจริง
Domenic

3
public string Title { get; private set; }ผลลัพธ์จะไม่เหมือนกันหรือไม่ คุณจะสามารถเปลี่ยนได้จากภายในห้องเรียนแน่นอน แต่ถ้าคุณทำเช่นนั้นคุณมีปัญหาอื่น ๆ ... : p
Svish

2
@Svish - โดยโต้แย้งว่าคำอ่านได้อย่างเดียวใน C # ไม่ควรใช้เพราะการใช้งานจะหมายถึงว่าเรากำลังหลบซ่อนตัวอยู่เหล่านี้ "ปัญหาที่แตกต่างกัน"
Zaid ร์

2
ฉันแค่หมายความว่าจากมุมมอง API ภายนอกมันจะไม่สร้างความแตกต่างมากนัก ดังนั้นคุณสมบัติอัตโนมัติสามารถใช้ถ้าต้องการ แน่นอนว่าสิ่งที่ดีที่สุดคือถ้าคุณสามารถทำสิ่งที่ต้องการpublic string Title { get; private readonly set; }
Svish

12

ฉันมักจะสร้างคุณสมบัติแทนฟิลด์สาธารณะเพราะคุณสามารถใช้คุณสมบัติในการกำหนดอินเทอร์เฟซคุณไม่สามารถใช้ฟิลด์สาธารณะในการกำหนดอินเทอร์เฟซ


8

คุณสมบัติอัตโนมัติมีเวทย์มนตร์ดำมากกว่าอย่างอื่นใน C # เมื่อคุณคิดถึงมันในแง่ของการรวบรวมลง IL แทนที่จะขยายไปยังคุณสมบัติ C # ปกติก่อนมันเป็นเวทย์มนตร์ดำน้อยกว่าภาษาอื่น ๆ มากมาย


5

ฉันใช้คุณสมบัติอัตโนมัติตลอดเวลา ก่อน C # 3 ฉันไม่สามารถรำคาญกับการพิมพ์ทั้งหมดและใช้ตัวแปรสาธารณะแทน

สิ่งเดียวที่ฉันพลาดคือสามารถทำได้:

public string Name = "DefaultName";

คุณต้องเปลี่ยนค่าเริ่มต้นเป็นคอนสตรัคเตอร์ของคุณด้วยคุณสมบัติ น่าเบื่อ :-(


4
ด้วยการเริ่มต้นคุณสมบัติอัตโนมัติจาก C # 6 คุณจะสามารถทำสิ่งนี้ได้ในไม่public string Name { get; set; } = "DefaultName"; ช้า
Carlos Muñoz

5

ฉันคิดว่าโครงสร้างใด ๆ ที่ใช้งานง่ายและลดบรรทัดของรหัสเป็นข้อดี

ฟีเจอร์เหล่านั้นคือสิ่งที่ทำให้ภาษาเช่น Ruby มีประสิทธิภาพ (นั่นและฟีเจอร์แบบไดนามิกซึ่งช่วยลดรหัสส่วนเกิน)

Ruby มีทั้งหมดนี้เป็น:

attr_accessor :my_property
attr_reader :my_getter
attr_writer :my_setter

2

ปัญหาเดียวที่ฉันมีกับพวกเขาคือพวกเขาไม่ได้ไปไกลพอ คอมไพเลอร์รุ่นเดียวกันที่เพิ่มคุณสมบัติอัตโนมัติเพิ่มวิธีการบางส่วน ทำไมพวกเขาไม่รวมสองคนเข้าด้วยกันจึงเป็นเรื่องเกินฉัน "บางส่วนบน <PropertyName> Changed" แบบง่ายๆจะทำให้สิ่งเหล่านี้มีประโยชน์จริง ๆ


คุณสามารถใส่วิธีการบางส่วนได้หลายวิธีภายในวิธีอื่น การสร้างรูปแบบอัตโนมัติของการจัดเรียงบางอย่างสำหรับพวกเขาอาจสร้างความสับสน
Matthew Whited

2

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

ง่ายเหมือนที่


1

สิ่งหนึ่งที่ควรทราบที่นี่คือเพื่อความเข้าใจของฉันนี่เป็นเพียงน้ำตาล syntactic ที่ปลาย C # 3.0 ซึ่งหมายความว่า IL ที่สร้างโดยคอมไพเลอร์เหมือนกัน ฉันเห็นด้วยเกี่ยวกับการหลีกเลี่ยงมนต์ดำ แต่สิ่งที่เหมือนกันมีน้อยกว่าสำหรับสิ่งเดียวกันมักเป็นสิ่งที่ดี


1

ในความคิดของฉันคุณควรใช้คุณสมบัติอัตโนมัติแทนฟิลด์สาธารณะ ที่กล่าวว่านี่คือการประนีประนอม:

เริ่มต้นด้วยฟิลด์ภายในโดยใช้หลักการตั้งชื่อที่คุณจะใช้กับคุณสมบัติ เมื่อคุณแรกเช่นกัน

  • ต้องการการเข้าถึงสนามจากภายนอกการชุมนุมหรือ
  • จำเป็นต้องแนบตรรกะเข้ากับ getter / setter

ทำเช่นนี้:

  1. เปลี่ยนชื่อฟิลด์
  2. ทำให้เป็นส่วนตัว
  3. เพิ่มทรัพย์สินสาธารณะ

รหัสลูกค้าของคุณไม่จำเป็นต้องเปลี่ยน

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


0

ฉันใช้ CodeRush เร็วกว่าคุณสมบัติอัตโนมัติ

เพื่อทำสิ่งนี้:

 private string title;
public string Title
{
    get { return title;  }
    set { title = value;  }
}

ต้องใช้การกดแป้นพิมพ์แปดครั้ง


5
หากฉันกด CTRL และ V ค้างไว้ฉันสามารถวางสิ่งต่างๆมากมาย / เร็วมาก / แต่ก็ไม่ได้ทำให้ดีขึ้น คำถามนี้จะตอบคำถามเดิมอย่างไร
JBRWilkinson

0

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


0

@Domenic: ฉันไม่เข้าใจ .. คุณไม่สามารถทำสิ่งนี้กับคุณสมบัติอัตโนมัติได้หรือไม่:

public string Title { get; }

หรือ

public string Title { get; private set; }

นี่คือสิ่งที่คุณหมายถึง?


คุณสามารถ (อันหลัง; อดีตจะไม่คอมไพล์), แต่ฟิลด์นั้นไม่เปลี่ยนรูปในวัตถุของคุณ
Domenic

คำเตือนเฉพาะ structs เท่านั้นที่ไม่เปลี่ยนรูปเมื่อถูกตั้งค่าสถานะแบบอ่านอย่างเดียวคลาสไม่สามารถกำหนดได้
Guvante

0

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

สิ่งที่ VS2008 หายไปคือคุณสมบัติการระเบิดอัตโนมัติ refactorกระจายอัตโนมัติ

ความจริงเรามีตัวสร้างฟิลด์แบบแค็ปซูลทำให้วิธีที่ฉันทำงานเร็วขึ้นเพียงแค่ใช้ฟิลด์สาธารณะ

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