โยนข้อยกเว้นหรือให้รหัสล้มเหลว


52

ฉันสงสัยว่ามีข้อดีและข้อเสียต่อสไตล์นี้หรือไม่:

private void LoadMaterial(string name)
{
    if (_Materials.ContainsKey(name))
    {
        throw new ArgumentException("The material named " + name + " has already been loaded.");
    }

    _Materials.Add(
        name,
        Resources.Load(string.Format("Materials/{0}", name)) as Material
    );
}

วิธีการนั้นควรnameจะเรียกใช้เพียงครั้งเดียวเท่านั้น จะโยนยกเว้นถ้ามันจะถูกเรียกว่าหลายครั้งเหมือนกัน_Materials.Add() nameผลที่ตามมาคือการป้องกันซ้ำซ้อนอย่างสมบูรณ์หรือมีประโยชน์ที่เห็นได้ชัดน้อยลงหรือไม่?

นั่นคือ C #, Unity หากใครสนใจ


3
จะเกิดอะไรขึ้นถ้าคุณโหลดวัสดุสองครั้งโดยไม่มีผู้พิทักษ์ ไม่_Materials.Addโยนยกเว้น?
253751

2
ที่จริงฉันได้กล่าวถึงแล้วในข้อผิดพลาดโยน: P
async

9
หมายเหตุด้านข้าง: 1) พิจารณาใช้การstring.Formatต่อข้อมูลสตริงเพื่อสร้างข้อความแสดงข้อยกเว้น 2) ใช้เฉพาะถ้าคุณคาดหวังโยนที่จะล้มเหลวและตรวจสอบผลการas nullหากคุณคาดว่าจะเคยได้รับใช้หล่อเหมือนMaterial (Material)Resources.Load(...)ข้อผิดพลาดในการส่งที่ชัดเจนนั้นง่ายต่อการตรวจแก้จุดบกพร่องมากกว่าข้อยกเว้นการอ้างอิงค่า null ที่เกิดขึ้นในภายหลัง
CodesInChaos

3
ในกรณีเฉพาะผู้โทรของคุณอาจพบเพื่อนร่วมทางLoadMaterialIfNotLoadedหรือReloadMaterial(ชื่อนี้สามารถใช้การปรับปรุง) มีประโยชน์
jpmc26

1
ในหมายเหตุอื่น: รูปแบบนี้ใช้ได้สำหรับบางครั้งการเพิ่มรายการลงในรายการ อย่างไรก็ตามอย่าใช้รูปแบบนี้เพื่อเติมรายการทั้งหมดเพราะคุณจะพบกับสถานการณ์ O (n ^ 2) ที่มีรายการขนาดใหญ่คุณจะพบกับปัญหาด้านประสิทธิภาพ คุณจะไม่สังเกตเห็นมันที่ 100 รายการ แต่ที่ 1,000 รายการคุณแน่นอนและที่ 10.000 รายการมันจะเป็นคอขวดที่สำคัญ
Pieter B

คำตอบ:


109

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

ได้รับในกรณีนี้พวกเขาอาจจะเดาได้ว่าข้อยกเว้น "มาตรฐาน" นั้นหมายความว่าอย่างไร แต่คุณยังคงเห็นได้ชัดว่าพวกเขาละเมิดสัญญาของคุณแทนที่จะสะดุดกับข้อผิดพลาดบางอย่างในรหัสของคุณ


3
ตกลง. เป็นการดีกว่าที่จะโยนข้อยกเว้นสำหรับการละเมิดเงื่อนไขก่อนหน้า
Frank Hileman

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

2
"ชัดเจนดีกว่าโดยปริยาย" - Zen of Python
jpmc26

4
แม้ว่าข้อผิดพลาดที่เกิดจากการโยน_Materials.Addไม่ใช่ข้อผิดพลาดที่คุณต้องการส่งไปยังผู้โทร แต่ฉันคิดว่าวิธีการ relabelling ข้อผิดพลาดนี้ไม่มีประสิทธิภาพ สำหรับการโทรที่ประสบความสำเร็จทุกครั้งLoadMaterial(การใช้งานปกติ) คุณจะต้องทำแบบทดสอบสติสองครั้งในLoadMaterialครั้ง_Materials.Addต่อไป หากมีเลเยอร์มากขึ้นแต่ละชั้นใช้สไตล์เดียวกันคุณสามารถทดสอบซ้ำได้หลายครั้ง
Marc van Leeuwen

15
... ให้ลองพิจารณาว่าจะเกิดข้อผิดพลาด_Materials.Addอย่างไม่มีเงื่อนไขจากนั้นจับข้อผิดพลาดที่อาจเกิดขึ้นและในตัวจัดการโยนสิ่งที่แตกต่างออกไป ตอนนี้การทำงานพิเศษทำได้ในกรณีที่ผิดพลาดเท่านั้นเส้นทางการทำงานที่ยอดเยี่ยมซึ่งคุณไม่ต้องกังวลกับประสิทธิภาพเลย
Marc van Leeuwen

100

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

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


2
ในทางกลับกันการมีฟังก์ชั่นล้มเหลวอย่างเงียบ ๆ อาจทำให้เกิดพฤติกรรมที่ไม่คาดคิดส่งผลให้พบข้อบกพร่องที่เกิดขึ้นในภายหลังได้ยากขึ้น
Philipp

30
@Phippipp ฟังก์ชันจะไม่ล้มเหลวอย่างเงียบ ๆ การเป็น idempotent หมายความว่าการรันหลาย ๆ ครั้งจะมีผลเช่นเดียวกับการรันครั้งเดียว กล่าวอีกนัยหนึ่งถ้าวัสดุถูกโหลดไปแล้วข้อมูลจำเพาะของเรา ("วัสดุที่ควรจะโหลด") ก็เป็นที่น่าพอใจอยู่แล้วดังนั้นเราไม่จำเป็นต้องทำอะไรเลย เราสามารถกลับมาได้
Warbo

8
ฉันชอบสิ่งนี้มากกว่าจริง แน่นอนขึ้นอยู่กับข้อ จำกัด ที่กำหนดโดยข้อกำหนดของแอปพลิเคชันซึ่งอาจผิด จากนั้นอีกครั้งฉันเป็นแฟนตัวยงของวิธีการ idempotent
FP

6
@ ฟิลิปป์ถ้าเราคิดว่ามันLoadMaterialมีข้อ จำกัด ดังต่อไปนี้: "หลังจากเรียกมันว่าวัสดุนั้นถูกโหลดอยู่เสมอและจำนวนของวัสดุที่โหลดไม่ลดลง" โยนข้อยกเว้นสำหรับข้อ จำกัด ที่สาม "วัสดุที่ไม่สามารถเพิ่มสองครั้ง" ดูเหมือนโง่และ counterintuitive สำหรับฉัน การทำให้ฟังก์ชั่น idempotent ช่วยลดการพึ่งพาของรหัสในการสั่งการดำเนินการและสถานะแอปพลิเคชัน ดูเหมือนว่าฉันจะชนะสองครั้ง
Benjamin Gruenbaum

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

8

คำถามพื้นฐานที่คุณต้องถามคือ: คุณต้องการให้ส่วนต่อประสานงานของคุณทำงานเป็นอย่างไร โดยเฉพาะอย่างยิ่งเงื่อนไข_Materials.ContainsKey(name)เป็นเงื่อนไขเบื้องต้นของฟังก์ชั่นหรือไม่

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

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

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

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

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

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

สำหรับคำอธิบายรายละเอียดของปัญหาด้วยวิธีการป้องกันคลาสสิกเช่นเดียวกับการดำเนินงานที่เป็นไปได้สำหรับยืนยันสไตล์การตรวจสอบเงื่อนไขโปรดดูที่จอห์น Lakos' พูดคุยโปรแกรมป้องกัน Done Rightจาก CppCon ปี 2014 ( สไลด์ , วิดีโอ )


4

มีคำตอบอยู่สองสามข้อแล้ว แต่นี่เป็นคำตอบที่คำนึงถึง Unity3D (คำตอบนั้นเฉพาะเจาะจงมากกับ Unity3D ฉันจะทำสิ่งนี้แตกต่างกันมากในบริบทส่วนใหญ่):

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

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

ฉันจะบอกว่าใน Unity คุณอาจต้องการแนวทางพิเศษเพิ่มเติม กระแทกแดกดันคำตอบลงคะแนนมากในขณะที่เขียนนี้แสดงวิธีหนึ่งที่ฉันอาจใช้บางสิ่งเช่นนี้โดยเฉพาะในบริบทของ Unity3D (ที่อื่น ๆ เช่นนี้ไม่สามารถยอมรับได้จริงและแม้ใน Unity มันค่อนข้างไม่เหมาะสม)

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

และเกี่ยวกับการใช้สิ่งต่าง ๆ เช่นDebug.LogXXฟังก์ชั่นแทนที่จะเป็นข้อยกเว้นคุณยังต้องพิจารณาว่าจะเกิดอะไรขึ้นเมื่อมีข้อยกเว้นเกิดขึ้นจากสิ่งที่ส่งคืนค่า (เช่น GetMaterial) ฉันมักจะวิธีนี้โดยผ่าน null พร้อมกับบันทึกข้อผิดพลาด ( อีกครั้งเฉพาะใน Unity ) จากนั้นฉันจะใช้การตรวจสอบโมฆะใน MonoBeh พฤติกรรมของฉันเพื่อให้แน่ใจว่าการพึ่งพาใด ๆ เช่นวัสดุไม่ใช่ค่า null และปิดการใช้งาน MonoBehavior หากเป็น ตัวอย่างสำหรับพฤติกรรมแบบง่าย ๆ ที่ต้องมีการพึ่งพากันสองสามอย่างคือ:

    public void Awake()
    {
        _inputParameters = GetComponent<VehicleInputParameters>();
        _rigidbody = GetComponent<Rigidbody>();
        _rigidbodyTransform = _rigidbody.transform;
        _raycastStrategySelector = GetComponent<RaycastStrategySelectionBehavior>();

        _trackParameters =
            SceneManager.InstanceOf.CurrentSceneData.GetValue<TrackParameters>();

        this.DisableIfNull(() => _rigidbody);
        this.DisableIfNull(() => _raycastStrategySelector);
        this.DisableIfNull(() => _inputParameters);
        this.DisableIfNull(() => _trackParameters);
    }

SceneData.GetValue<>คล้ายกับตัวอย่างของคุณที่เรียกฟังก์ชันในพจนานุกรมที่มีข้อยกเว้น แต่แทนที่จะโยนข้อยกเว้นจะใช้Debug.LogErrorซึ่งจะให้การติดตามสแต็กเหมือนข้อยกเว้นปกติและจะส่งกลับเป็นโมฆะ การตรวจสอบที่ตามมา * จะปิดใช้งานพฤติกรรมแทนที่จะปล่อยให้มันยังคงอยู่ในสถานะที่ไม่ถูกต้อง

* เช็คดูเหมือนว่าเป็นเพราะผู้ช่วยตัวเล็กฉันใช้มันพิมพ์ข้อความที่จัดรูปแบบเมื่อปิดการใช้งานวัตถุเกม ** การตรวจสอบ null แบบง่าย ๆ กับifงานที่นี่ (** เช็คของผู้ช่วยรวบรวมเฉพาะใน Debug builds (เช่น asserts) การใช้ lambdas และการแสดงออกเช่นนั้นใน Unity สามารถทำให้ประสิทธิภาพลดลงได้)


1
+1 สำหรับการเพิ่มข้อมูลใหม่อย่างแท้จริงไปยังกอง ฉันไม่ได้คาดหวังว่า
Ixrec

3

ฉันชอบคำตอบหลัก ๆ สองข้อ แต่ต้องการแนะนำว่าชื่อฟังก์ชันของคุณสามารถปรับปรุงได้ ฉันคุ้นเคยกับ Java ดังนั้น YMMV แต่ควรมีวิธี "เพิ่ม" IMO โดยไม่โยนข้อยกเว้นหากรายการมีอยู่แล้ว มันควรจะเพิ่มรายการอีกครั้งหรือถ้าปลายทางคือชุดไม่ต้องทำอะไร เนื่องจากนั่นไม่ใช่พฤติกรรมของวัสดุเพิ่มที่ควรเปลี่ยนชื่อ TryPut หรือ AddOnce หรือ AddOrThrow หรือคล้ายกัน

ในทำนองเดียวกัน LoadMaterial ของคุณควรเปลี่ยนชื่อ LoadIfAbsent หรือ Put หรือ TryLoad หรือ LoadOrThrow (ขึ้นอยู่กับว่าคุณใช้คำตอบ # 1 หรือ # 2) หรือบางอย่าง

ทำตามอนุสัญญาการตั้งชื่อ C # Unity ไม่ว่าจะเป็นอะไร

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


มีความแตกต่างระหว่าง C # Dictionary.Add () semantics (ดูmsdn.microsoft.com/en-us/library/k7z0zy8k%28v=vs.110%29.aspx ) และ Java Collection.add () semantics (ดูเอกสาร) oracle.com/javase/8/docs/api/java/util/… ) C # ส่งคืนโมฆะและส่งข้อยกเว้น ในขณะที่ Java คืนค่าบูลและแทนที่ค่าที่เก็บไว้ก่อนหน้านี้ด้วยค่าใหม่หรือส่งข้อยกเว้นเมื่อไม่สามารถเพิ่มองค์ประกอบใหม่ได้
Kasper van den Berg

Kasper - ขอบคุณและน่าสนใจ คุณจะแทนที่ค่าในพจนานุกรม C # ได้อย่างไร (เทียบเท่ากับ Java put ()) ไม่เห็นวิธีการติดตั้งในหน้านั้น
949300

คำถามที่ดีใครสามารถทำได้ Dictionary.Remove () (ดูmsdn.microsoft.com/en-us/library/bb356469%28v=vs.110%29.aspx ) ตามด้วย Dictionary.Add () (ดู msdn.microsoft .com / en-us / library / bb338565% 28v = vs.110% 29.aspx ) แต่สิ่งนี้ดูค่อนข้างน่าอึดอัดใจ
Kasper van den Berg

@ user949300 พจนานุกรม [สำคัญ] = ค่าแทนที่รายการหากมีอยู่แล้ว
Rupe

@Rupe และอาจขว้างบางอย่างถ้าไม่เป็น ดูเหมือนว่าฉันจะอึดอัดใจมาก ลูกค้าส่วนใหญ่ตั้งค่า Dict ไม่สนใจ wheteher รายการก็มีพวกเขาเพียงแค่สนใจว่าหลังจากการตั้งค่ารหัสของพวกเขาก็คือมี ให้คะแนนหนึ่งสำหรับ Java Collections API (IMHO) PHP ยังงุ่มง่ามกับ dicts มันเป็นความผิดพลาด fot C # ที่จะตามมาก่อนหน้านั้น
949300

3

คำตอบทั้งหมดเพิ่มแนวคิดที่มีค่าฉันต้องการรวมพวกเขา:

ตัดสินใจเกี่ยวกับความหมายและความคาดหวังของการLoadMaterial()ดำเนินการ อย่างน้อยมีตัวเลือกดังต่อไปนี้:

  • เงื่อนไขเบื้องต้นในการnameโหลดวัสดุ : →

    เมื่อเงื่อนไขถูกละเมิดผลของการLoadMaterial()ไม่ได้ระบุ (ดังในคำตอบโดยComicSansMS ) LoadMaterial()นี้จะช่วยให้เสรีภาพมากที่สุดในการดำเนินการและการเปลี่ยนแปลงในอนาคตของ หรือ,

  • มีการระบุเอฟเฟกต์ของการโทรLoadMaterial(name)ด้วยnameLoadedMaterials ; ทั้ง:

    • ข้อมูลจำเพาะระบุว่ามีการโยนข้อยกเว้น หรือ
    • สเปคระบุว่าผลลัพธ์นั้นเป็น idempotent (ตามคำตอบ ของKarl Bielefeldt )

      • อาจจะกลับมาเป็น "มีคอลเลกชันที่มีการเปลี่ยนแปลง" บูล (ตามที่ เสนอโดยUser949300และเสนอ (ในการตีความบางคน) โดยMrlveck

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

  • โยนข้อยกเว้นที่กำหนดเอง (ตามที่แนะนำโดยIxrec ) →

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

    • เพื่อหลีกเลี่ยงค่าใช้จ่ายในการตรวจสอบซ้ำ ๆnameโหลดวัสดุคุณสามารถทำตามคำแนะนำของ Marc van Leeuwen :

      ... ให้ลองพิจารณาว่าวัสดุลดลงเพิ่มโดยไม่มีเงื่อนไขแล้วจับข้อผิดพลาดที่อาจเกิดขึ้นและในการจัดการโยนที่แตกต่างกัน

  • ให้Dictionayเพิ่มข้อยกเว้น→

    รหัสการโยนข้อยกเว้นซ้ำซ้อน - Jon Raynor

    แม้ว่าผู้ลงคะแนนส่วนใหญ่จะเห็นด้วยกับIxrecมากขึ้น

    เหตุผลเพิ่มเติมในการเลือกการใช้งานนี้คือ:

    • ผู้โทรนั้นสามารถจัดการArgumentExceptionข้อยกเว้นและ
    • เพื่อหลีกเลี่ยงการสูญเสียข้อมูลกองซ้อน

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

  • ทำให้LoadMaterial()idempotent เป็นanwserโดยKarl Bielefeldtและ upvoted บ่อยที่สุด (75 ครั้ง)

    • อาจจะกลับมาเป็น "มีคอลเลกชันที่มีการเปลี่ยนแปลง" บูล (ตามที่ เสนอโดยUser949300และเสนอ (ในการตีความบางคน) โดยMrlveck

    ตัวเลือกการใช้งานสำหรับพฤติกรรมนี้:

    • ตรวจสอบกับDictionary.ContainsKey ()

    • เสมอเรียกDictionary.Add ()จับArgumentExceptionมันจะพ่นเมื่อคีย์เพื่อแทรกมีอยู่แล้วและไม่สนใจข้อยกเว้นนั้น เอกสารที่ละเว้นข้อยกเว้นคือสิ่งที่คุณตั้งใจจะทำและทำไม

      • เมื่อLoadMaterials()ใด (เกือบ) จะถูกเรียกหนึ่งครั้งสำหรับแต่ละครั้งnameสิ่งนี้จะช่วยลดค่าใช้จ่ายในการตรวจสอบซ้ำ ๆnameโหลดวัสดุวัสดุ cf มาร์คฟานลีเวน อย่างไรก็ตาม
      • เมื่อLoadedMaterials()มักจะถูกเรียกหลายครั้งสำหรับสิ่งเดียวกัน nameสิ่งนี้จะเกิดค่าใช้จ่ายในการขว้างArgumentExceptionและคลี่คลาย(แพง)
    • ฉันคิดว่ามีTryAddอะนาล็อกวิธีการกับTryGet () ซึ่งจะช่วยให้คุณหลีกเลี่ยงข้อยกเว้นราคาแพงในการขว้างปา

      แต่วิธีนี้TryAddดูเหมือนจะไม่มีอยู่จริง


1
+1 สำหรับการเป็นคำตอบที่ครอบคลุมที่สุด สิ่งนี้อาจไปไกลเกินกว่าที่ OP จะดำเนินการในตอนแรก แต่เป็นบทสรุปที่มีประโยชน์ของตัวเลือกทั้งหมด
Ixrec

1

รหัสการโยนข้อยกเว้นซ้ำซ้อน

ถ้าคุณโทร:

_Materials.Add (ชื่อ, Resources.Load (string.Format ("Materials / {0}", ชื่อ)) เป็นวัสดุ

ด้วยรหัสเดียวกันมันจะขว้าง

System.ArgumentException

ข้อความจะเป็น: "รายการที่มีคีย์เดียวกันถูกเพิ่มแล้ว"

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

มิฉะนั้นฉันอาจจะ refactor ออกยามในกรณีนี้


0

ฉันคิดว่านี่เป็นคำถามเกี่ยวกับการออกแบบ API มากกว่าคำถามแบบแผนการเข้ารหัส

ผลลัพธ์ที่คาดหวัง (สัญญา) ของการโทรคืออะไร:

LoadMaterial("wood");

หากผู้เรียกอาจคาดหวัง / รับประกันว่าหลังจากเรียกวิธีนี้วัสดุ "ไม้" ถูกโหลดแล้วฉันไม่เห็นเหตุผลที่จะโยนข้อยกเว้นเมื่อวัสดุนั้นถูกโหลดแล้ว

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


-2

ทำไมไม่เปลี่ยนวิธีการเพื่อให้มันกลับมา 'จริง' มันเป็นเรื่องเพิ่มและ 'เท็จ' ถ้ามันไม่ได้?

private bool LoadMaterial(string name)
{
   if (_Materials.ContainsKey(name))

    {

        return false; //already present
    }

    _Materials.Add(
        name,
        Resources.Load(string.Format("Materials/{0}", name)) as Material
    );

    return true;

}

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

เป็นกรณีนี้หรือไม่? ฉันคิดว่ามันเป็นเพียงกรณีที่มี semipredicate ( en.wikipedia.org/wiki/Semipredicate_problem ) เนื่องจากในตัวอย่างโค้ด OP การไม่สามารถเพิ่มเนื้อหาลงในระบบนั้นไม่มีปัญหาจริง วิธีการมีวัตถุประสงค์เพื่อให้แน่ใจว่าวัสดุอยู่ในระบบเมื่อคุณเรียกใช้และนั่นคือสิ่งที่วิธีการนี้ทำ (แม้ว่าจะล้มเหลว) บูลีนที่ส่งคืนจะระบุว่าวิธีนี้ได้พยายามเพิ่มเรื่องนี้หรือไม่ ps: ฉันไม่ได้คำนึงถึงว่าอาจมีหลายเรื่องที่มีชื่อเดียวกัน
MrIveck

1
ฉันคิดว่าฉันพบวิธีแก้ปัญหาด้วยตัวเองแล้ว: en.wikipedia.org/wiki/…นี่ชี้ให้เห็นว่าคำตอบของ Ixrec นั้นถูกต้องที่สุด
MrIveck

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