หากบล็อกสุดท้ายพ่นยกเว้นสิ่งที่ว่าที่เกิดขึ้น?
โดยเฉพาะจะเกิดอะไรขึ้นถ้าข้อยกเว้นถูกโยนลงครึ่งหนึ่งผ่านบล็อกสุดท้าย ส่วนที่เหลือของคำสั่ง (หลังจาก) ในบล็อกนี้ถูกเรียก?
ฉันรู้ว่าข้อยกเว้นจะแพร่กระจายขึ้นไป
หากบล็อกสุดท้ายพ่นยกเว้นสิ่งที่ว่าที่เกิดขึ้น?
โดยเฉพาะจะเกิดอะไรขึ้นถ้าข้อยกเว้นถูกโยนลงครึ่งหนึ่งผ่านบล็อกสุดท้าย ส่วนที่เหลือของคำสั่ง (หลังจาก) ในบล็อกนี้ถูกเรียก?
ฉันรู้ว่าข้อยกเว้นจะแพร่กระจายขึ้นไป
คำตอบ:
หากบล็อกในที่สุดก็โยนข้อยกเว้นสิ่งที่ว่าที่เกิดขึ้น?
ข้อยกเว้นนั้นแพร่กระจายออกไปและขึ้นและจะ (สามารถ) ได้รับการจัดการในระดับที่สูงขึ้น
ในที่สุดบล็อกของคุณจะไม่เสร็จสมบูรณ์เกินกว่าจุดที่มีการโยนข้อยกเว้น
หากบล็อกสุดท้ายถูกเรียกใช้งานระหว่างการจัดการข้อยกเว้นก่อนหน้านี้แสดงว่าข้อยกเว้นแรกหายไป
ข้อมูลจำเพาะภาษา C # 4 § 8.9.5: หากบล็อกสุดท้ายส่งข้อยกเว้นอื่นการประมวลผลข้อยกเว้นปัจจุบันจะถูกยกเลิก
ThreadAbortException
บล็อกทั้งหมดในที่สุดจะเสร็จสิ้นก่อนเนื่องจากเป็นส่วนที่สำคัญ
สำหรับคำถามเช่นนี้ฉันมักจะเปิดโครงการแอปพลิเคชั่นคอนโซลว่างใน Visual Studio และเขียนโปรแกรมตัวอย่างขนาดเล็ก:
using System;
class Program
{
static void Main(string[] args)
{
try
{
try
{
throw new Exception("exception thrown from try block");
}
catch (Exception ex)
{
Console.WriteLine("Inner catch block handling {0}.", ex.Message);
throw;
}
finally
{
Console.WriteLine("Inner finally block");
throw new Exception("exception thrown from finally block");
Console.WriteLine("This line is never reached");
}
}
catch (Exception ex)
{
Console.WriteLine("Outer catch block handling {0}.", ex.Message);
}
finally
{
Console.WriteLine("Outer finally block");
}
}
}
เมื่อคุณเรียกใช้โปรแกรมคุณจะเห็นลำดับที่แน่นอนที่จะดำเนินการcatch
และfinally
บล็อก โปรดทราบว่ารหัสในบล็อกสุดท้ายหลังจากโยนข้อยกเว้นจะไม่ถูกดำเนินการ (อันที่จริงแล้วในโปรแกรมตัวอย่างนี้ Visual Studio จะเตือนคุณว่าตรวจพบรหัสที่เข้าไม่ถึง):
ข้อยกเว้นการจัดการบล็อก catch ภายในถูกส่งออกจาก try block ในที่สุดก็ปิดกั้น ข้อยกเว้นการจัดการบล็อก catch ภายนอกถูกโยนออกจากบล็อกในที่สุด บล็อกนอกสุด
หมายเหตุเพิ่มเติม
ดังที่ไมเคิลดามาตอฟชี้ให้เห็นข้อยกเว้นจากการtry
บล็อกจะ "กิน" ถ้าคุณไม่จัดการมันในcatch
บล็อก(ด้านใน) ในความเป็นจริงในตัวอย่างข้างต้นข้อยกเว้นการโยนใหม่ไม่ปรากฏในบล็อก catch ภายนอก เพื่อทำให้ชัดเจนยิ่งขึ้นในตัวอย่างที่แก้ไขเล็กน้อยต่อไปนี้:
using System;
class Program
{
static void Main(string[] args)
{
try
{
try
{
throw new Exception("exception thrown from try block");
}
finally
{
Console.WriteLine("Inner finally block");
throw new Exception("exception thrown from finally block");
Console.WriteLine("This line is never reached");
}
}
catch (Exception ex)
{
Console.WriteLine("Outer catch block handling {0}.", ex.Message);
}
finally
{
Console.WriteLine("Outer finally block");
}
}
}
ดังที่คุณเห็นจากผลลัพธ์ข้อยกเว้นภายในคือ "หลงทาง" (เช่นถูกละเว้น):
ในที่สุดก็ปิดกั้น ข้อยกเว้นการจัดการบล็อก catch ภายนอกถูกโยนออกจากบล็อกในที่สุด บล็อกนอกสุด
finally
บล็อกจะถูกดำเนินการเกือบทุกครั้งซึ่งจะเป็นกรณีนี้สำหรับบล็อกในที่สุด (เพียงลองโปรแกรมตัวอย่างด้วยตัวเอง (บล็อกสุดท้ายจะไม่ถูกดำเนินการในกรณีที่ไม่สามารถกู้คืนได้) ยกเว้นเช่นEngineExecutionException
, แต่ในกรณีเช่นนี้โปรแกรมของคุณจะยุติทันที)
หากมีข้อยกเว้นที่ค้างอยู่ (เมื่อtry
บล็อกมีfinally
แต่ไม่มีcatch
) ข้อยกเว้นใหม่จะแทนที่ข้อผิดพลาดนั้น
หากไม่มีข้อยกเว้นรอดำเนินการมันก็เหมือนกับการโยนข้อยกเว้นนอกfinally
บล็อก
catch
บล็อกที่ (ใหม่) พ่นข้อยกเว้น
ส่วนย่อยอย่างรวดเร็ว (และค่อนข้างชัดเจน) เพื่อบันทึก "ข้อยกเว้นดั้งเดิม" (โยนในtry
บล็อก) และเสียสละ "ในที่สุดยกเว้น" (โยนลงในfinally
บล็อก) ในกรณีที่ต้นฉบับมีความสำคัญสำหรับคุณ:
try
{
throw new Exception("Original Exception");
}
finally
{
try
{
throw new Exception("Finally Exception");
}
catch
{ }
}
เมื่อมีการใช้งานโค้ดข้างต้น "ข้อยกเว้นดั้งเดิม" จะส่งผ่านสแต็กการโทรขึ้นและ "การยกเว้นขั้นสุดท้าย" จะหายไป
ฉันต้องทำเช่นนี้เพื่อจับข้อผิดพลาดพยายามปิดกระแสที่ไม่เคยเปิดเพราะมีข้อยกเว้น
errorMessage = string.Empty;
try
{
byte[] requestBytes = System.Text.Encoding.ASCII.GetBytes(xmlFileContent);
webRequest = WebRequest.Create(url);
webRequest.Method = "POST";
webRequest.ContentType = "text/xml;charset=utf-8";
webRequest.ContentLength = requestBytes.Length;
//send the request
using (var sw = webRequest.GetRequestStream())
{
sw.Write(requestBytes, 0, requestBytes.Length);
}
//get the response
webResponse = webRequest.GetResponse();
using (var sr = new StreamReader(webResponse.GetResponseStream()))
{
returnVal = sr.ReadToEnd();
sr.Close();
}
}
catch (Exception ex)
{
errorMessage = ex.ToString();
}
finally
{
try
{
if (webRequest.GetRequestStream() != null)
webRequest.GetRequestStream().Close();
if (webResponse.GetResponseStream() != null)
webResponse.GetResponseStream().Close();
}
catch (Exception exw)
{
errorMessage = exw.ToString();
}
}
ถ้า webRequest ถูกสร้างขึ้น แต่เกิดข้อผิดพลาดในการเชื่อมต่อระหว่าง
using (var sw = webRequest.GetRequestStream())
ในที่สุดก็จะมีข้อยกเว้นพยายามปิดการเชื่อมต่อที่คิดว่าเปิดเพราะ webRequest ถูกสร้างขึ้น
หากในที่สุดไม่ได้ลองภายในโค้ดนี้จะทำให้เกิดข้อยกเว้นในขณะที่ทำความสะอาด webRequest
if (webRequest.GetRequestStream() != null)
จากนั้นรหัสจะออกโดยไม่ต้องจัดการข้อผิดพลาดที่เกิดขึ้นและทำให้เกิดปัญหาสำหรับวิธีการโทร
หวังว่านี่จะช่วยเป็นตัวอย่าง
การส่งข้อยกเว้นขณะที่ข้อยกเว้นอื่นทำงานอยู่จะส่งผลให้การยกเว้นครั้งแรกได้รับการแทนที่ด้วยข้อยกเว้นที่สอง (ภายหลัง)
นี่คือรหัสที่แสดงให้เห็นว่าเกิดอะไรขึ้น:
public static void Main(string[] args)
{
try
{
try
{
throw new Exception("first exception");
}
finally
{
//try
{
throw new Exception("second exception");
}
//catch (Exception)
{
//throw;
}
}
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
หลายเดือนก่อนฉันยังต้องเผชิญกับบางสิ่งเช่นนี้
private void RaiseException(String errorMessage)
{
throw new Exception(errorMessage);
}
private void DoTaskForFinally()
{
RaiseException("Error for finally");
}
private void DoTaskForCatch()
{
RaiseException("Error for catch");
}
private void DoTaskForTry()
{
RaiseException("Error for try");
}
try
{
/*lacks the exception*/
DoTaskForTry();
}
catch (Exception exception)
{
/*lacks the exception*/
DoTaskForCatch();
}
finally
{
/*the result exception*/
DoTaskForFinally();
}
เพื่อแก้ปัญหาดังกล่าวฉันทำคลาสยูทิลิตี้เช่น
class ProcessHandler : Exception
{
private enum ProcessType
{
Try,
Catch,
Finally,
}
private Boolean _hasException;
private Boolean _hasTryException;
private Boolean _hasCatchException;
private Boolean _hasFinnallyException;
public Boolean HasException { get { return _hasException; } }
public Boolean HasTryException { get { return _hasTryException; } }
public Boolean HasCatchException { get { return _hasCatchException; } }
public Boolean HasFinnallyException { get { return _hasFinnallyException; } }
public Dictionary<String, Exception> Exceptions { get; private set; }
public readonly Action TryAction;
public readonly Action CatchAction;
public readonly Action FinallyAction;
public ProcessHandler(Action tryAction = null, Action catchAction = null, Action finallyAction = null)
{
TryAction = tryAction;
CatchAction = catchAction;
FinallyAction = finallyAction;
_hasException = false;
_hasTryException = false;
_hasCatchException = false;
_hasFinnallyException = false;
Exceptions = new Dictionary<string, Exception>();
}
private void Invoke(Action action, ref Boolean isError, ProcessType processType)
{
try
{
action.Invoke();
}
catch (Exception exception)
{
_hasException = true;
isError = true;
Exceptions.Add(processType.ToString(), exception);
}
}
private void InvokeTryAction()
{
if (TryAction == null)
{
return;
}
Invoke(TryAction, ref _hasTryException, ProcessType.Try);
}
private void InvokeCatchAction()
{
if (CatchAction == null)
{
return;
}
Invoke(TryAction, ref _hasCatchException, ProcessType.Catch);
}
private void InvokeFinallyAction()
{
if (FinallyAction == null)
{
return;
}
Invoke(TryAction, ref _hasFinnallyException, ProcessType.Finally);
}
public void InvokeActions()
{
InvokeTryAction();
if (HasTryException)
{
InvokeCatchAction();
}
InvokeFinallyAction();
if (HasException)
{
throw this;
}
}
}
และใช้แบบนี้
try
{
ProcessHandler handler = new ProcessHandler(DoTaskForTry, DoTaskForCatch, DoTaskForFinally);
handler.InvokeActions();
}
catch (Exception exception)
{
var processError = exception as ProcessHandler;
/*this exception contains all exceptions*/
throw new Exception("Error to Process Actions", exception);
}
แต่ถ้าคุณต้องการใช้พารามิเตอร์และประเภทผลตอบแทนนั่นเป็นเรื่องอื่น
public void MyMethod()
{
try
{
}
catch{}
finally
{
CodeA
}
CodeB
}
วิธีจัดการข้อยกเว้นโดย CodeA และ CodeB นั้นเหมือนกัน
ข้อยกเว้นที่ถูกโยนในfinally
บล็อกไม่มีอะไรพิเศษให้ถือว่าเป็นข้อยกเว้นการขว้างด้วยรหัส B
ข้อยกเว้นแพร่กระจายขึ้นและควรได้รับการจัดการในระดับที่สูงขึ้น หากข้อยกเว้นไม่ได้รับการจัดการในระดับที่สูงกว่าแอปพลิเคชันจะขัดข้อง การดำเนินการบล็อก "ในที่สุด" จะหยุดลง ณ จุดที่เกิดข้อยกเว้น
ไม่ว่าจะมีข้อยกเว้นหรือไม่ก็ตาม "block" รับประกันว่าจะสามารถใช้งานได้
หากมีการดำเนินการบล็อก "ในที่สุด" หลังจากข้อยกเว้นเกิดขึ้นในบล็อกลอง
และหากข้อยกเว้นนั้นไม่ได้รับการจัดการ
และในที่สุดหากบล็อกพ่นข้อยกเว้น
จากนั้นข้อยกเว้นดั้งเดิมที่เกิดขึ้นในบล็อกลองจะหายไป
public class Exception
{
public static void Main()
{
try
{
SomeMethod();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
public static void SomeMethod()
{
try
{
// This exception will be lost
throw new Exception("Exception in try block");
}
finally
{
throw new Exception("Exception in finally block");
}
}
}
มันส่งข้อยกเว้น;) คุณสามารถตรวจจับข้อยกเว้นนั้นในประโยคจับอื่น ๆ