เมื่อใดจึงเหมาะสมที่จะโยนข้อยกเว้นจากภายในคุณสมบัติ getter หรือ setter? เมื่อใดไม่เหมาะสม ทำไม? ลิงก์ไปยังเอกสารภายนอกในหัวข้อนี้จะเป็นประโยชน์ ... Google เปิดเผยเล็กน้อยอย่างน่าประหลาดใจ
เมื่อใดจึงเหมาะสมที่จะโยนข้อยกเว้นจากภายในคุณสมบัติ getter หรือ setter? เมื่อใดไม่เหมาะสม ทำไม? ลิงก์ไปยังเอกสารภายนอกในหัวข้อนี้จะเป็นประโยชน์ ... Google เปิดเผยเล็กน้อยอย่างน่าประหลาดใจ
คำตอบ:
Microsoft มีคำแนะนำเกี่ยวกับวิธีการออกแบบคุณสมบัติที่http://msdn.microsoft.com/en-us/library/ms229006.aspx
โดยพื้นฐานแล้วพวกเขาแนะนำให้ผู้รับทรัพย์สินเป็นตัวเข้าถึงที่มีน้ำหนักเบาซึ่งปลอดภัยในการโทรเสมอ พวกเขาแนะนำให้ออกแบบ getters ใหม่ให้เป็นวิธีการหากข้อยกเว้นเป็นสิ่งที่คุณต้องโยนทิ้ง สำหรับตัวตั้งค่าจะระบุว่าข้อยกเว้นเป็นกลยุทธ์การจัดการข้อผิดพลาดที่เหมาะสมและยอมรับได้
สำหรับตัวทำดัชนี Microsoft ระบุว่าเป็นที่ยอมรับสำหรับทั้ง getters และ setters ในการโยนข้อยกเว้น และอันที่จริงดัชนีหลายตัวในไลบรารี. NET ก็ทำเช่นนี้ ArgumentOutOfRangeException
ส่วนใหญ่เป็นข้อยกเว้นที่พบบ่อย
มีเหตุผลที่ดีบางประการที่คุณไม่ต้องการทิ้งข้อยกเว้นในการรับทรัพย์สิน:
obj.PropA.AnotherProp.YetAnother
- ด้วยไวยากรณ์ประเภทนี้จะกลายเป็นปัญหาในการตัดสินใจว่าจะใส่คำสั่งการจับข้อยกเว้นที่ใดหมายเหตุด้านข้างเราควรทราบว่าเพียงเพราะคุณสมบัติไม่ได้ออกแบบมาเพื่อให้เกิดข้อยกเว้นนั่นไม่ได้หมายความว่าจะไม่มี มันสามารถเรียกรหัสได้อย่างง่ายดาย แม้แต่การจัดสรรออบเจ็กต์ใหม่ (เช่นสตริง) ง่ายๆก็อาจทำให้เกิดข้อยกเว้นได้ คุณควรเขียนโค้ดของคุณในเชิงป้องกันและคาดหวังข้อยกเว้นจากสิ่งที่คุณเรียกใช้
ไม่มีอะไรผิดในการขว้างข้อยกเว้นจากเซ็ตเตอร์ ท้ายที่สุดจะมีวิธีใดที่ดีกว่าในการระบุว่าค่าไม่ถูกต้องสำหรับคุณสมบัติที่ระบุ
สำหรับ getters โดยทั่วไปมักจะขมวดคิ้วและอธิบายได้ค่อนข้างง่าย: property getter โดยทั่วไปจะรายงานสถานะปัจจุบันของวัตถุ ดังนั้นกรณีเดียวที่เหมาะสมสำหรับผู้ที่จะขว้างปาคือเมื่อสถานะไม่ถูกต้อง แต่โดยทั่วไปถือว่าเป็นความคิดที่ดีในการออกแบบคลาสของคุณเพื่อที่จะไม่สามารถรับอ็อบเจ็กต์ที่ไม่ถูกต้องได้ในตอนแรกหรือทำให้มันอยู่ในสถานะที่ไม่ถูกต้องด้วยวิธีการปกติ (กล่าวคือให้แน่ใจว่ามีการเริ่มต้นอย่างสมบูรณ์ในตัวสร้างเสมอและ ลองสร้างวิธีการที่ปลอดภัยโดยคำนึงถึงความถูกต้องของสถานะและค่าคงที่ของคลาส) ตราบใดที่คุณยึดมั่นในกฎนั้นผู้รับทรัพย์สินของคุณไม่ควรตกอยู่ในสถานการณ์ที่พวกเขาต้องรายงานสถานะที่ไม่ถูกต้องและอย่าโยนทิ้ง
มีข้อยกเว้นที่ฉันรู้คือและเป็นจริงหนึ่งที่ค่อนข้างสำคัญ: IDisposable
การใช้วัตถุใด Dispose
มีจุดมุ่งหมายโดยเฉพาะเพื่อนำวัตถุเข้าสู่สถานะที่ไม่ถูกต้องและยังมีคลาสข้อยกเว้นพิเศษObjectDisposedException
ที่จะใช้ในกรณีนั้น เป็นเรื่องปกติอย่างยิ่งที่จะโยนObjectDisposedException
จากสมาชิกคลาสใด ๆ รวมถึงผู้ได้รับทรัพย์สิน (และไม่รวมDispose
ตัวเอง) หลังจากที่วัตถุถูกกำจัดไปแล้ว
IDisposable
ควรถูกทำให้ไร้ประโยชน์หลังจาก a Dispose
. หากเรียกใช้สมาชิกจะต้องใช้ทรัพยากรที่Dispose
ไม่สามารถใช้งานได้ (เช่นสมาชิกจะอ่านข้อมูลจากสตรีมที่ถูกปิด) สมาชิกควรโยนObjectDisposedException
มากกว่าการรั่วไหลเช่นArgumentException
แต่หากมีรูปแบบที่มีคุณสมบัติที่แสดงถึง ค่าในบางช่องดูเหมือนว่าจะมีประโยชน์มากกว่าที่จะอนุญาตให้อ่านคุณสมบัติดังกล่าวหลังการกำจัด (ให้ค่าที่พิมพ์ครั้งสุดท้าย) มากกว่าที่จะต้องการ ...
Dispose
รอการตัดบัญชีจนกว่าจะอ่านคุณสมบัติดังกล่าวทั้งหมด ในบางกรณีที่เธรดหนึ่งอาจใช้การบล็อกการอ่านบนอ็อบเจ็กต์ในขณะที่อีกเธรดหนึ่งปิดและเมื่อข้อมูลอาจมาถึงก่อนเวลาใดก็ได้ก่อนหน้าDispose
นี้การDispose
ตัดข้อมูลขาเข้าอาจเป็นประโยชน์แต่อนุญาตให้อ่านข้อมูลที่ได้รับก่อนหน้านี้ได้ เราไม่ควรบังคับให้เกิดความแตกต่างเทียมระหว่างClose
และDispose
ในสถานการณ์ที่ไม่จำเป็นต้องมีอยู่
Get...
วิธีการแทน ข้อยกเว้นคือเมื่อคุณต้องใช้อินเทอร์เฟซที่มีอยู่ซึ่งคุณต้องระบุคุณสมบัติ
แทบจะไม่เคยเหมาะสมกับผู้เริ่มต้นและบางครั้งก็เหมาะสมกับผู้ตั้งรับ
แหล่งข้อมูลที่ดีที่สุดสำหรับคำถามประเภทนี้คือ "Framework Design Guidelines" โดย Cwalina และ Abrams มีให้บริการในรูปแบบหนังสือผูกมัดและส่วนใหญ่ยังมีให้บริการทางออนไลน์
จากหัวข้อ 5.2: การออกแบบอสังหาริมทรัพย์
หลีกเลี่ยงการขว้างปาข้อยกเว้นจากตัวรับทรัพย์สิน ผู้รับทรัพย์สินควรดำเนินการง่ายๆและไม่ควรมีเงื่อนไขเบื้องต้น หากผู้โจมตีสามารถโยนข้อยกเว้นได้ก็ควรได้รับการออกแบบใหม่ให้เป็นวิธีการ โปรดทราบว่ากฎนี้ใช้ไม่ได้กับผู้จัดทำดัชนีซึ่งเราคาดว่าจะมีข้อยกเว้นอันเป็นผลมาจากการตรวจสอบความถูกต้องของอาร์กิวเมนต์
โปรดทราบว่าแนวทางนี้ใช้กับคุณสมบัติ getters เท่านั้น ตกลงที่จะโยนข้อยกเว้นในตัวตั้งค่าคุณสมบัติ
ObjectDisposedException
เมื่อวัตถุถูกDispose()
เรียกและมีบางสิ่งที่ถามหามูลค่าทรัพย์สินในภายหลัง ดูเหมือนว่าคำแนะนำควรเป็น "หลีกเลี่ยงการโยนข้อยกเว้นจากผู้รับทรัพย์สินเว้นแต่จะมีการกำจัดวัตถุซึ่งในกรณีนี้คุณควรพิจารณาขว้าง ObjectDisposedExcpetion"
แนวทางที่ดีวิธีหนึ่งสำหรับข้อยกเว้นคือการใช้เพื่อจัดทำโค้ดสำหรับตัวคุณเองและนักพัฒนารายอื่น ๆ ดังนี้:
ข้อยกเว้นควรมีไว้สำหรับสถานะโปรแกรมพิเศษ ซึ่งหมายความว่าคุณสามารถเขียนได้ทุกที่ที่คุณต้องการ!
เหตุผลหนึ่งที่คุณอาจต้องการใส่ไว้ใน getters คือการจัดทำเอกสาร API ของคลาส - หากซอฟต์แวร์เกิดข้อยกเว้นทันทีที่โปรแกรมเมอร์พยายามใช้งานผิดพวกเขาจะไม่ใช้ผิด! ตัวอย่างเช่นหากคุณมีการตรวจสอบความถูกต้องในระหว่างกระบวนการอ่านข้อมูลอาจไม่สมเหตุสมผลที่จะสามารถดำเนินการต่อและเข้าถึงผลลัพธ์ของกระบวนการได้หากมีข้อผิดพลาดร้ายแรงในข้อมูล ในกรณีนี้คุณอาจต้องทำการส่งเอาต์พุตหากมีข้อผิดพลาดเพื่อให้แน่ใจว่าโปรแกรมเมอร์คนอื่นตรวจสอบเงื่อนไขนี้
เป็นวิธีการบันทึกสมมติฐานและขอบเขตของระบบย่อย / วิธีการ / อะไรก็ตาม ในกรณีทั่วไปไม่ควรจับ! นอกจากนี้ยังเป็นเพราะพวกเขาจะไม่ถูกโยนทิ้งหากระบบทำงานร่วมกันในแบบที่คาดไว้: หากมีข้อยกเว้นเกิดขึ้นแสดงว่าไม่เป็นไปตามสมมติฐานของโค้ด - เช่นไม่ได้มีปฏิสัมพันธ์กับโลกรอบตัว เดิมมีจุดมุ่งหมายเพื่อ หากคุณตรวจพบข้อยกเว้นที่เขียนขึ้นเพื่อจุดประสงค์นี้อาจหมายความว่าระบบเข้าสู่สถานะที่คาดเดาไม่ได้ / ไม่สอดคล้องกันซึ่งในที่สุดอาจนำไปสู่ความผิดพลาดหรือความเสียหายของข้อมูลหรือสิ่งที่คล้ายกันซึ่งมีแนวโน้มที่จะตรวจจับ / แก้ไขข้อบกพร่องได้ยากกว่ามาก
ข้อความข้อยกเว้นเป็นวิธีการรายงานข้อผิดพลาดที่หยาบมาก - ไม่สามารถรวบรวมได้ทั้งจำนวนและมีเพียงสตริงเท่านั้น ทำให้ไม่เหมาะสำหรับการรายงานปัญหาในข้อมูลอินพุต ในการรันปกติระบบเองไม่ควรเข้าสู่สถานะข้อผิดพลาด ด้วยเหตุนี้ข้อความในข้อความจึงควรได้รับการออกแบบมาสำหรับโปรแกรมเมอร์ไม่ใช่สำหรับผู้ใช้สิ่งที่ผิดพลาดในข้อมูลอินพุตสามารถค้นพบและส่งต่อไปยังผู้ใช้ในรูปแบบ (กำหนดเอง) ที่เหมาะสมกว่าได้
ข้อยกเว้น (ฮ่าฮ่า!) สำหรับกฎนี้คือสิ่งต่างๆเช่น IO ซึ่งข้อยกเว้นไม่ได้อยู่ภายใต้การควบคุมของคุณและไม่สามารถตรวจสอบล่วงหน้าได้
ทั้งหมดนี้บันทึกไว้ใน MSDN (ตามที่เชื่อมโยงกับคำตอบอื่น ๆ ) แต่นี่เป็นกฎทั่วไป ...
ใน setter หากคุณสมบัติของคุณควรได้รับการตรวจสอบมากกว่าประเภท ตัวอย่างเช่นคุณสมบัติที่เรียกว่า PhoneNumber น่าจะมีการตรวจสอบความถูกต้องของ regex และควรทำให้เกิดข้อผิดพลาดหากรูปแบบไม่ถูกต้อง
สำหรับ getters อาจเป็นไปได้ว่าเมื่อค่าเป็นโมฆะ แต่ส่วนใหญ่เป็นสิ่งที่คุณต้องการจัดการกับรหัสการโทร (ตามแนวทางการออกแบบ)
MSDN: การจับและขว้างประเภทข้อยกเว้นมาตรฐาน
นี่เป็นคำถามและคำตอบที่ซับซ้อนมากขึ้นอยู่กับวิธีใช้วัตถุของคุณ ตามกฎทั่วไปแล้วผู้รับทรัพย์สินและตัวตั้งค่าที่ "มีผลผูกพันล่าช้า" ไม่ควรมีข้อยกเว้นในขณะที่คุณสมบัติที่มี "การผูกมัดก่อนกำหนด" โดยเฉพาะควรมีข้อยกเว้นเมื่อจำเป็น BTW เครื่องมือวิเคราะห์โค้ดของ Microsoft กำลังกำหนดการใช้คุณสมบัติแคบเกินไปในความคิดของฉัน
"late binding" หมายถึงคุณสมบัติที่พบผ่านการสะท้อน ตัวอย่างเช่นแอตทริบิวต์ Serializeable "ใช้เพื่อทำให้เป็นซีเรียลไลซ์ / deserialize วัตถุผ่านคุณสมบัติของมันการโยนข้อยกเว้นในสถานการณ์แบบนี้จะทำให้สิ่งต่างๆเกิดหายนะและไม่ใช่วิธีที่ดีในการใช้ข้อยกเว้นเพื่อทำให้โค้ดมีประสิทธิภาพมากขึ้น
"การผูกในช่วงต้น" หมายความว่าการใช้คุณสมบัติถูกผูกไว้ในโค้ดโดยคอมไพเลอร์ ตัวอย่างเช่นเมื่อโค้ดบางโค้ดที่คุณเขียนอ้างถึงคุณสมบัติ getter ในกรณีนี้คุณสามารถโยนข้อยกเว้นได้เมื่อเหมาะสม
วัตถุที่มีแอตทริบิวต์ภายในมีสถานะที่กำหนดโดยค่าของแอตทริบิวต์เหล่านั้น ไม่ควรใช้คุณสมบัติที่แสดงแอ็ตทริบิวต์ที่ตระหนักและไวต่อสถานะภายในของอ็อบเจ็กต์สำหรับการเชื่อมต่อล่าช้า ตัวอย่างเช่นสมมติว่าคุณมีวัตถุที่ต้องเปิดเข้าถึงแล้วปิด ในกรณีนี้การเข้าถึงคุณสมบัติโดยไม่เรียกเปิดก่อนควรส่งผลให้เกิดข้อยกเว้น สมมติว่าในกรณีนี้เราไม่ทิ้งข้อยกเว้นและเราอนุญาตให้โค้ดเข้าถึงค่าโดยไม่ทิ้งข้อยกเว้น? รหัสจะดูมีความสุขแม้ว่าจะได้รับค่าจาก getter ที่ไม่สมเหตุสมผลก็ตาม ตอนนี้เราได้ใส่รหัสที่เรียกว่า getter ในสถานการณ์ที่ไม่ดีเนื่องจากต้องรู้วิธีตรวจสอบค่าเพื่อดูว่ามันไม่สมเหตุสมผลหรือไม่ ซึ่งหมายความว่าโค้ดจะต้องตั้งสมมติฐานเกี่ยวกับมูลค่าที่ได้รับจากคุณสมบัติ getter เพื่อที่จะตรวจสอบความถูกต้อง นี่คือการเขียนโค้ดที่ไม่ดี
ฉันมีรหัสนี้ซึ่งฉันไม่แน่ใจว่าจะโยนข้อยกเว้นใด
public Person
{
public string Name { get; set; }
public boolean HasPets { get; set; }
}
public void Foo(Person person)
{
if (person.Name == null) {
throw new Exception("Name of person is null.");
// I was unsure of which exception to throw here.
}
Console.WriteLine("Name is: " + person.Name);
}
ฉันป้องกันไม่ให้โมเดลมีคุณสมบัติเป็นโมฆะตั้งแต่แรกโดยบังคับให้เป็นอาร์กิวเมนต์ในตัวสร้าง
public Person
{
public Person(string name)
{
if (name == null) {
throw new ArgumentNullException(nameof(name));
}
Name = name;
}
public string Name { get; private set; }
public boolean HasPets { get; set; }
}
public void Foo(Person person)
{
Console.WriteLine("Name is: " + person.Name);
}