ค่าใช้จ่ายที่แท้จริงของการลอง / จับใน C # คืออะไร?


94

ดังนั้นฉันรู้ว่า try / catch เพิ่มค่าใช้จ่ายบางส่วนดังนั้นจึงไม่ใช่วิธีที่ดีในการควบคุมโฟลว์กระบวนการ แต่ค่าใช้จ่ายนี้มาจากไหนและผลกระทบที่แท้จริงคืออะไร

คำตอบ:


52

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

ฉันไม่คิดว่ามันจะเป็นปัญหาตราบใดที่คุณใช้ข้อยกเว้นสำหรับพฤติกรรม EXCEPTIONAL (ไม่ใช่เส้นทางปกติที่คุณคาดหวังผ่านโปรแกรม)


33
อย่างแม่นยำมากขึ้น: ลองถูกจับถูกโยนแพง หากคุณหลีกเลี่ยงการพยายามจับโยนยังคงมีราคาแพง
โปรแกรมเมอร์ Windows

4
อืม - มาร์กอัปใช้ไม่ได้กับความคิดเห็น หากต้องการลองอีกครั้ง - ข้อยกเว้นมีไว้สำหรับข้อผิดพลาดไม่ใช่สำหรับ "พฤติกรรมพิเศษ" หรือเงื่อนไข: blogs.msdn.com/kcwalina/archive/2008/07/17/…
HTTP 410

2
@Windows โปรแกรมเมอร์สถิติ / ที่มาโปรด?
Kapé

100

สามจุดที่ต้องทำที่นี่:

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

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

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


41
นอกจากนี้: เมื่อคุณสร้างข้อยกเว้นใหม่เป็น "throw ex" คุณจะสูญเสียการติดตามสแต็กเดิมและแทนที่ด้วยการติดตามสแต็กปัจจุบัน ไม่ค่อยมีอะไรต้องการ หากคุณเพียงแค่ "โยน" การติดตามสแต็กดั้งเดิมใน Exception จะถูกเก็บรักษาไว้
Eddie

throw new Exception("Wrapping layer’s error", ex);
@Eddie

21

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

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

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

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

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


6

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

bool HasRight(string rightName, DomainObject obj) {
  try {
    CheckRight(rightName, obj);
    return true;
  }
  catch (Exception ex) {
    return false;
  }
}

void CheckRight(string rightName, DomainObject obj) {
  if (!_user.Rights.Contains(rightName))
    throw new Exception();
}

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

ดังนั้นฉันจึงปรับโครงสร้างใหม่เป็นสิ่งต่อไปนี้ซึ่ง - ตามการวัดอย่างรวดเร็ว 'n สกปรกในภายหลัง - มีขนาดเร็วขึ้นประมาณ 2 คำสั่ง:

bool HasRight(string rightName, DomainObject obj) {
  return _user.Rights.Contains(rightName);
}

void CheckRight(string rightName, DomainObject obj) {
  if (!HasRight(rightName, obj))
    throw new Exception();
}

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


1
ทำไมคุณถึงต้องการยกเว้นที่นี่? คุณสามารถจัดการกับกรณีที่ไม่มีสิทธิ์ได้ทันที
ThunderGr

@ThunderGr นั่นคือสิ่งที่ฉันเปลี่ยนไปทำให้มันเร็วขึ้นสองคำสั่ง
Tobi

5

ตรงกันข้ามกับทฤษฎีที่ยอมรับกันทั่วไปtry/ catchอาจมีผลกระทบอย่างมีนัยสำคัญและนั่นอาจเป็นข้อยกเว้นหรือไม่!

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

ก่อนหน้านี้ได้รับการกล่าวถึงในบล็อกโพสต์สองสามรายการโดย Microsoft MVP ในช่วงหลายปีที่ผ่านมาและฉันเชื่อว่าคุณสามารถค้นหาได้อย่างง่ายดาย แต่ StackOverflow ก็ใส่ใจ ให้กับเนื้อหามากดังนั้นฉันจะให้ลิงก์ไปยังบางส่วนเพื่อเป็นหลักฐานการเติม :

นอกจากนี้ยังมีคำตอบนี้ซึ่งแสดงให้เห็นความแตกต่างระหว่างรหัส disassembled with- และโดยไม่ต้องใช้/trycatch

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

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


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

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

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

ใช้try/ catch:

int x;
try {
    x = int.Parse("1234");
}
catch {
    return;
}
// some more code here...

ไม่ใช้try/ catch:

int x;
if (int.TryParse("1234", out x) == false) {
    return;
}
// some more code here

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

เมื่อมีการนำฟิลด์เข้ามาในชั้นเรียนมากขึ้นเรื่อย ๆ ขยะสำเร็จรูปทั้งหมดนี้จะสะสม (ทั้งในรหัสต้นทางและรหัสแยกชิ้นส่วน) เกินระดับที่เหมาะสม สี่บรรทัดต่อสนามและมันเป็นบรรทัดเดียวกันเสมอ ... เราไม่ได้รับการสอนให้หลีกเลี่ยงการทำซ้ำตัวเองหรือ? ฉันคิดว่าเราสามารถซ่อนtry/catchเบื้องหลังนามธรรมที่ชงเองที่บ้านได้ แต่ ... จากนั้นเราก็อาจหลีกเลี่ยงข้อยกเว้น (เช่นใช้Int.TryParse )

นี่ไม่ใช่ตัวอย่างที่ซับซ้อน ฉันเคยเห็นความพยายามในการสร้างอินสแตนซ์คลาสใหม่ในtry/ catch. พิจารณาว่าโค้ดทั้งหมดภายในตัวสร้างอาจถูกตัดสิทธิ์จากการเพิ่มประสิทธิภาพบางอย่างที่คอมไพเลอร์จะนำไปใช้โดยอัตโนมัติ จะมีอะไรดีไปกว่าการทำให้เกิดทฤษฎีที่คอมไพเลอร์ช้าเมื่อเทียบกับคอมไพเลอร์จะทำสิ่งที่มันบอกว่าจะทำอย่างไร ?

สมมติว่ามีข้อยกเว้นเกิดขึ้นโดยตัวสร้างดังกล่าวและข้อผิดพลาดบางอย่างถูกทริกเกอร์ด้วยเหตุนี้ผู้พัฒนาด้านการบำรุงรักษาที่ไม่ดีจะต้องติดตามมัน นั่นอาจไม่ใช่เรื่องง่ายเช่นนี้ซึ่งแตกต่างจากรหัสสปาเก็ตตี้ของgoto nightmare try/ catchอาจทำให้เกิดความยุ่งเหยิงในสามมิติเนื่องจากสามารถเลื่อนสแต็กขึ้นไปไม่ใช่แค่ส่วนอื่น ๆ ของวิธีการเดียวกัน แต่ยังรวมถึงคลาสและวิธีการอื่น ๆ ด้วย ซึ่งทั้งหมดนี้จะถูกสังเกตโดยผู้พัฒนาด้านการบำรุงรักษาวิธีที่ยาก ! ยังมีคนบอกว่า "goto อันตราย" หึ!

ในตอนท้ายฉันพูดถึงtry/ catchมีประโยชน์ซึ่งออกแบบมาเพื่อปิดการใช้งานการเพิ่มประสิทธิภาพ ! ถ้าคุณจะช่วยแก้จุดบกพร่อง ! นั่นคือสิ่งที่ออกแบบมาสำหรับและเป็นสิ่งที่ควรใช้เป็น ...

ฉันเดาว่านั่นเป็นจุดบวกเช่นกัน สามารถใช้เพื่อปิดใช้งานการเพิ่มประสิทธิภาพที่อาจทำให้อัลกอริธึมการส่งข้อความที่ปลอดภัยและปลอดภัยสำหรับแอปพลิเคชันแบบมัลติเธรดและเพื่อตรวจจับสภาพการแข่งขันที่เป็นไปได้;) นั่นเป็นเพียงสถานการณ์เดียวที่ฉันคิดได้ว่าจะใช้ try / catch แม้จะมีทางเลือกอื่น


optimisations ทำอะไรtry, catchและfinallyปิดการใช้งาน?

อาคา

เป็นอย่างไรtry, catchและfinallyเป็นประโยชน์ในการแก้จุดบกพร่องโรคเอดส์?

เป็นอุปสรรคในการเขียน สิ่งนี้มาจากมาตรฐาน:

12.3.3.13 งบทดลองจับ

สำหรับคำสั่งstmtของแบบฟอร์ม:

try try-block
catch ( ... ) catch-block-1
... 
catch ( ... ) catch-block-n
  • รัฐที่ได้รับมอบหมายที่ชัดเจนของโวลต์ที่จุดเริ่มต้นของการพยายามบล็อกเป็นเช่นเดียวกับรัฐที่ได้รับมอบหมายที่ชัดเจนของโวลต์ที่จุดเริ่มต้นของstmt
  • รัฐที่ได้รับมอบหมายที่ชัดเจนของโวลต์ที่จุดเริ่มต้นของการจับบล็อก-I (สำหรับผม ) เป็นเช่นเดียวกับรัฐที่ได้รับมอบหมายที่ชัดเจนของโวลต์ที่จุดเริ่มต้นของstmt
  • สถานะการกำหนดที่แน่นอนของvที่จุดสิ้นสุดของstmtถูกกำหนดอย่างแน่นอนถ้า (และเฉพาะในกรณีที่) vถูกกำหนดอย่างแน่นอนที่จุดสิ้นสุดของtry-blockและทุกๆcatch-block-i (สำหรับทุกๆiตั้งแต่ 1 ถึงn ).

กล่าวอีกนัยหนึ่งในตอนต้นของแต่ละtryคำสั่ง:

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

เรื่องราวที่คล้ายกันถือไว้สำหรับแต่ละcatchคำสั่ง สมมติว่าในtryคำสั่งของคุณ(หรือตัวสร้างหรือฟังก์ชันที่เรียกใช้ ฯลฯ ) ที่คุณกำหนดให้กับตัวแปรที่ไม่มีจุดหมายนั้น (สมมติว่าgarbage=42;) คอมไพเลอร์ไม่สามารถกำจัดคำสั่งนั้นได้ไม่ว่ามันจะไม่เกี่ยวข้องกับพฤติกรรมที่สังเกตได้ของโปรแกรมก็ตาม . งานจะต้องเสร็จสิ้นก่อนที่catchจะเข้าสู่บล็อก

สำหรับสิ่งที่คุ้มค่าfinallyเล่าเรื่องราวที่เสื่อมโทรมในทำนองเดียวกัน:

12.3.3.14 งบทดลองในที่สุด

สำหรับคำสั่งtry stmtของแบบฟอร์ม:

try try-block
finally finally-block

•รัฐมอบหมายชัดเจนของโวลต์ ที่จุดเริ่มต้นของการพยายามบล็อกเป็นเช่นเดียวกับรัฐมอบหมายชัดเจนของโวลต์ที่จุดเริ่มต้นของstmt
•รัฐมอบหมายชัดเจนของโวลต์ที่จุดเริ่มต้นของที่สุดบล็อกเป็นเช่นเดียวกับรัฐมอบหมายชัดเจนของโวลต์ที่จุดเริ่มต้นของstmt
•สถานะการกำหนดที่แน่นอนของvที่จุดสิ้นสุดของstmtถูกกำหนดอย่างแน่นอนถ้า (และเฉพาะในกรณีที่) อย่างใดอย่างหนึ่ง: o vถูกกำหนดที่จุดสิ้นสุดของtry-block o vได้รับมอบหมายอย่างแน่นอนที่จุดสิ้นสุดของบล็อกสุดท้าย หากมีการถ่ายโอนโฟลว์การควบคุม (เช่นคำสั่งgoto ) ที่เริ่มต้นภายในบล็อกการลองและสิ้นสุดนอกบล็อกการลองดังนั้นvจะถือว่าได้รับมอบหมายอย่างแน่นอน ควบคุมการถ่ายโอนการไหลถ้าโวลต์ได้รับมอบหมายแน่นอนที่จุดสิ้นสุดของที่สุดบล็อก (นี่ไม่ใช่เฉพาะในกรณีที่ - ถ้าvถูกกำหนดอย่างแน่นอนด้วยเหตุผลอื่นในการถ่ายโอนโฟลว์การควบคุมนี้ก็ยังถือว่าได้รับมอบหมายอย่างแน่นอน)

12.3.3.15 งบทดลองในที่สุด

การวิเคราะห์การมอบหมายงานที่ชัดเจนสำหรับคำสั่งtry - catch - สุดท้ายของแบบฟอร์ม:

try try-block
catch ( ... ) catch-block-1
... 
catch ( ... ) catch-block-n
finally finally-block

จะทำราวกับว่าคำสั่งนั้นเป็นคำสั่งtry - ในที่สุดคำสั่งที่แนบคำสั่งtry - catch :

try { 
    try   
    try-block
    catch ( ... ) catch-block-1
    ...   
    catch ( ... ) catch-block-n
} 
finally finally-block

3

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

ดังนั้นเพื่อสรุปทุกสิ่งที่เขียนไว้ที่นี่:
1) ใช้ try..catch บล็อคเพื่อตรวจจับข้อผิดพลาดที่ไม่คาดคิด - แทบจะไม่มีการลงโทษด้านประสิทธิภาพ
2) อย่าใช้ข้อยกเว้นสำหรับข้อผิดพลาดที่ยกเว้นหากคุณสามารถหลีกเลี่ยงได้


3

ฉันเขียนบทความเกี่ยวกับเรื่องนี้มาสักพักแล้วเพราะมีคนถามเกี่ยวกับเรื่องนี้เป็นจำนวนมากในเวลานั้น คุณสามารถค้นหาและรหัสการทดสอบที่http://www.blackwasp.co.uk/SpeedTestTryCatch.aspx

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

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


2

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


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

นอกจากบล็อกโพสต์ของ @ Hafthor แล้วนี่คือบล็อกโพสต์อื่นที่มีรหัสที่เขียนขึ้นโดยเฉพาะเพื่อทดสอบความแตกต่างของประสิทธิภาพความเร็ว จากผลลัพธ์หากคุณมีข้อยกเว้นเกิดขึ้นแม้เพียง 5% ของเวลาโค้ดการจัดการข้อยกเว้นจะทำงานช้ากว่าโค้ดการจัดการที่ไม่ใช่ข้อยกเว้น 100 เท่า บทความนี้กำหนดเป้าหมายเฉพาะวิธีการtry-catchบล็อกเทียบกับtryparse()วิธีการ แต่แนวคิดเหมือนกัน

2

ง่ายกว่าอย่างมากในการเขียนแก้ไขข้อบกพร่องและรักษาโค้ดที่ปราศจากข้อความแสดงข้อผิดพลาดของคอมไพเลอร์ข้อความเตือนการวิเคราะห์โค้ดและข้อยกเว้นที่ยอมรับตามปกติ (โดยเฉพาะข้อยกเว้นที่ถูกโยนทิ้งในที่เดียวและยอมรับในอีกที่หนึ่ง) เนื่องจากง่ายกว่าโดยเฉลี่ยแล้วโค้ดจะเขียนได้ดีขึ้นและมีข้อบกพร่องน้อยลง

สำหรับฉันแล้วโปรแกรมเมอร์และค่าใช้จ่ายด้านคุณภาพนั้นเป็นข้อโต้แย้งหลักในการใช้ try-catch สำหรับโฟลว์กระบวนการ

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


@Ritchard T ทำไม? เมื่อเปรียบเทียบกับโปรแกรมเมอร์และค่าใช้จ่ายด้านคุณภาพนั้นไม่มีนัยสำคัญ
ThunderGr

1

ฉันชอบบล็อกโพสต์ของ Hafthor มากและเพื่อเพิ่มสองเซ็นต์ของฉันในการสนทนานี้ฉันอยากจะบอกว่ามันเป็นเรื่องง่ายสำหรับฉันที่จะให้ DATA LAYER โยนข้อยกเว้นเพียงประเภทเดียว (DataAccessException) ด้วยวิธีนี้ BUSINESS LAYER ของฉันรู้ว่ามีข้อยกเว้นอะไรบ้างที่จะคาดหวังและจับมันได้ จากนั้นขึ้นอยู่กับกฎทางธุรกิจเพิ่มเติม (เช่นถ้าวัตถุทางธุรกิจของฉันมีส่วนร่วมในเวิร์กโฟลว์ ฯลฯ ) ฉันอาจโยนข้อยกเว้นใหม่ (BusinessObjectException) หรือดำเนินการต่อโดยไม่ต้องเปลี่ยน / โยน

บอกเลยว่าอย่าลังเลที่จะใช้ try..catch ทุกเมื่อที่จำเป็นและใช้อย่างชาญฉลาด!

ตัวอย่างเช่นวิธีนี้มีส่วนร่วมในเวิร์กโฟลว์ ...

ความคิดเห็น?

public bool DeleteGallery(int id)
{
    try
    {
        using (var transaction = new DbTransactionManager())
        {
            try
            {
                transaction.BeginTransaction();

                _galleryRepository.DeleteGallery(id, transaction);
                _galleryRepository.DeletePictures(id, transaction);

                FileManager.DeleteAll(id);

                transaction.Commit();
            }
            catch (DataAccessException ex)
            {
                Logger.Log(ex);
                transaction.Rollback();                        
                throw new BusinessObjectException("Cannot delete gallery. Ensure business rules and try again.", ex);
            }
        }
    }
    catch (DbTransactionException ex)
    {
        Logger.Log(ex);
        throw new BusinessObjectException("Cannot delete gallery.", ex);
    }
    return true;
}

2
เดวิดคุณจะปิดกั้นการโทรไปที่ 'DeleteGallery' ในการลอง / จับหรือไม่?
Rob

เนื่องจาก DeleteGallery เป็นฟังก์ชันบูลีนฉันจึงคิดว่าการทิ้งข้อยกเว้นไว้ในนั้นจึงไม่มีประโยชน์ สิ่งนี้จะต้องมีการเรียก DeleteGallery อยู่ในบล็อก try / catch ถ้า (! DeleteGallery (theid)) {// handle} ดูมีความหมายสำหรับฉันมากกว่า ในตัวอย่างเฉพาะนั้น
ThunderGr

1

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

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

ขออภัยเกี่ยวกับภาษาอังกฤษของฉันหวังว่ามันจะช่วยคุณได้ ข้อมูลนี้อ้างอิงจากหนังสือที่อ้างถึงสำหรับข้อมูลเพิ่มเติมโปรดดูบทที่ 8.5 การจัดการข้อยกเว้น


คอมไพเลอร์ไม่มีภาพขณะรันไทม์ มีจะมีจะเป็นค่าใช้จ่ายสำหรับการลอง / จับบล็อกเพื่อให้ CLR สามารถจัดการข้อยกเว้น C # รันบน. NET CLR (เครื่องเสมือน) สำหรับฉันแล้วดูเหมือนว่าค่าใช้จ่ายของบล็อกนั้นน้อยมากเมื่อไม่มีข้อยกเว้น แต่ค่าใช้จ่ายของ CLR ในการจัดการข้อยกเว้นนั้นมีความสำคัญมาก
ThunderGr

-4

ให้เราวิเคราะห์หนึ่งในต้นทุนที่ใหญ่ที่สุดที่เป็นไปได้ของบล็อก try / catch เมื่อใช้ในที่ที่ไม่จำเป็นต้องใช้:

int x;
try {
    x = int.Parse("1234");
}
catch {
    return;
}
// some more code here...

และนี่คือสิ่งที่ไม่ต้องลอง / จับ:

int x;
if (int.TryParse("1234", out x) == false) {
    return;
}
// some more code here

หากไม่นับช่องว่างที่ไม่มีนัยสำคัญเราอาจสังเกตเห็นว่าโค้ดสองส่วนที่เท่ากันนี้มีความยาวเกือบเท่ากันทุกประการในหน่วยไบต์ หลังมีการเยื้องน้อยกว่า 4 ไบต์ เป็นสิ่งที่ไม่ดีหรือไม่?

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

while (int.TryParse(...))
{
    ...
}

แต่เมื่อใช้ try / catch นี้จะดูเป็นอย่างไร?

try {
    for (;;)
    {
        x = int.Parse(...);
        ...
    }
}
catch
{
    ...
}

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

ต้นทุนที่น้อยกว่าอย่างหนึ่งคือการบล็อก try / catch ปิดการใช้งานการเพิ่มประสิทธิภาพบางอย่าง: http://msmvps.com/blogs/peterritchie/archive/2007/06/22/performance-implications-of-try-catch-finally.aspx . ฉันเดาว่านั่นเป็นจุดบวกเช่นกัน สามารถใช้เพื่อปิดใช้งานการเพิ่มประสิทธิภาพที่อาจทำให้อัลกอริธึมการส่งข้อความที่ปลอดภัยและปลอดภัยสำหรับแอปพลิเคชันแบบมัลติเธรดและเพื่อตรวจจับสภาพการแข่งขันที่เป็นไปได้;) นั่นเป็นเพียงสถานการณ์เดียวที่ฉันคิดได้ว่าจะใช้ try / catch แม้จะมีทางเลือกอื่น


1
ฉันค่อนข้างมั่นใจว่า TryParse พยายาม {int x = int.Parse ("xxx"); return true;} catch {return false; } ภายใน การเยื้องไม่ใช่สิ่งที่น่ากังวลในคำถามมีเพียงประสิทธิภาพและค่าใช้จ่ายเท่านั้น
ThunderGr

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