เหตุใดจึงมีวิธีที่ส่งกลับ bool / int และมีวัตถุจริงเป็นพารามิเตอร์ส่งออก


12

ฉันเห็นรูปแบบรหัสต่อไปนี้ทั่วทุกแห่งใน codebase ของ บริษัท ของฉัน (แอปพลิเคชัน. NET 3.5):

bool Foo(int barID, out Baz bazObject) { 
    try { 
            // do stuff
            bazObject = someResponseObject;

            return true;
    }
    catch (Exception ex) { 
        // log error
        return false;
    }
}

// calling code
BazObject baz = new BazObject();
fooObject.Foo(barID, out baz);

if (baz != null) { 
    // do stuff with baz
}

ฉันพยายามที่จะคลุมหัวทำไมฉันถึงทำแบบนี้แทนที่จะมีFooวิธีเอา ID และคืนค่าBazวัตถุแทนที่จะคืนค่าที่ไม่ได้ใช้และมีวัตถุจริงเป็นพารามิเตอร์อ้างอิงหรือผลลัพธ์

มีประโยชน์ซ่อนเร้นของรูปแบบการเข้ารหัสนี้ที่ฉันขาดหายไปหรือไม่?



ในตัวอย่างของbazการเป็นnullและกลับมาboolเป็นfalseเป็นไม่เทียบเท่า new BazObject()ไม่เคยมีnullดังนั้นถ้าbazObjectมีการปรับปรุงก่อนที่Exceptionจะถูกโยนทิ้งในFooเมื่อfalseถูกส่งกลับจะไม่เป็นbaz nullมันจะช่วยได้อย่างมากถ้าข้อมูลจำเพาะสำหรับFooมีอยู่ ในความเป็นจริงนี่อาจเป็นปัญหาที่ร้ายแรงที่สุดที่แสดงโดยรหัสนี้
Steve Powell

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

คำตอบ:


11

คุณมักจะใช้รูปแบบนั้นเพื่อให้คุณสามารถเขียนรหัสดังนี้:

if (Foo(barId, out bazObject))
{
  //DoStuff with bazobject
}

มันถูกใช้ใน CLR สำหรับ TryGetValue ในคลาสพจนานุกรมเช่น มันหลีกเลี่ยงความซ้ำซ้อน แต่ออกและพารามิเตอร์อ้างอิงดูเหมือนจะยุ่งเล็กน้อยสำหรับฉัน


1
+1 นี่คือเหตุผลที่ว่าทำไม แต่ผมกลับมีแนวโน้มต่อวัตถุที่มีทั้งแบบบูลและวัตถุแทนที่จะกลับพวกเขาทั้งสองแยกจากกัน
สาธารณรัฐประชาธิปไตยประชาชนลาว

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

คำตอบนี้แสดงให้เห็นถึงการใช้รูปแบบนี้ แต่ถ้ามันเกิน codebase ของคุณก็อาจเป็นการปฏิบัติที่ไม่ดีเช่นฝั่ง Sean
Codism

11

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

ใน. net เรามีข้อยกเว้นสำหรับวัตถุประสงค์นั้น ไม่ควรมีเหตุผลที่จะทำตามรูปแบบนี้

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


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

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

ใช่ดูเหมือนว่าส่วนใหญ่จะใช้ในวิธีการที่โหลดข้อมูลจากฐานข้อมูลเช่นif (baz.Select())แต่บ่อยครั้งที่ค่าที่ส่งคืนไม่ได้ถูกโยนทิ้งไปและค่าจะถูกตรวจสอบกับค่า null หรือคุณสมบัติบางอย่าง
Wayne Molina

@Sean ดูสิ่งที่คุณพูดฉันแค่คัดค้านวลี "ใน. NET เรามีข้อยกเว้นสำหรับวัตถุประสงค์นั้น" ข้อยกเว้นให้บริการวัตถุประสงค์ที่แตกต่างกันอย่างสิ้นเชิงสำหรับฉัน
สาธารณรัฐประชาธิปไตยประชาชนลาว

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

2

รับข้อมูลโค้ดมันดูไร้จุดหมายอย่างสมบูรณ์ สำหรับฉันแล้วรูปแบบรหัสเริ่มต้นจะแนะนำว่า null สำหรับ BazObject จะเป็นกรณีที่ยอมรับได้และการส่งคืน bool เป็นการวัดการกำหนดกรณีที่ล้มเหลวอย่างปลอดภัย หากรหัสต่อไปนี้คือ:

// calling code
BazObject baz = new BazObject();
bool result = fooObject.Foo(barID, out baz);

if (result) { 
    // do stuff with baz
    // where baz may be 
    // null without a 
    // thrown exception
}

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


ฉันคิดว่ามันเขียนโดยสมาชิกปัจจุบันของทีม บางทีนั่นอาจเป็นสิ่งที่ฉันควรกังวลเกี่ยวกับ O_O
Wayne Molina

@Wayne M: น้อยกังวลกว่าโอกาสที่การฝึกอบรมที่มีศักยภาพ :)
โจเอล Etherton

1

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


นั่นทำให้รู้สึก ดูเหมือนจะแปลกเล็กน้อย แต่ดูเหมือนว่าเหตุผลที่ถูกต้อง
Wayne Molina

0

แนวคิดคือส่งคืนค่าที่ระบุว่ากระบวนการสำเร็จหรือไม่อนุญาตให้ใช้รหัสดังนี้:

Baz b;
if (fooObject.foo(id, out b)) {
   // do something with b
}
else {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

หากวัตถุไม่สามารถเป็นโมฆะ (ซึ่งปรากฏในตัวอย่างของคุณถูกต้อง) ก็จะทำได้ดีกว่าดังนี้:

Baz b = fooObject.foo(id);
if (b != null) {
   // do something with b
}
else {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

หากวัตถุสามารถเป็นโมฆะข้อยกเว้นเป็นวิธีที่จะไป:

try {
   Baz b = fooObject.foo(id);
}
catch (BazException e) {
   Error.screamAndRun("object lookup/whatever failed! AAaAAAH!");
}

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


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