کنترل خطا و استثنا در سی شارپ
در این بخش، نحوه مدیریت استثنا در سی شارپ با استفاده از بلوک های try ، catch و blocks توضیح داده می شود.
استثناها در برنامه باید برای جلوگیری از خراب شدن برنامه و دریافت نتایج عجیب و غیرمنتظره، ثبت استثناها (log exceptions) و ادامه عملکردهای دیگر کنترل شوند. سی شارپ پشتیبانی داخلی برای مدیریت استثنا با استفاده از بلاک های try، catch و finally ارائه می کند.
try block: هر کد مشکوکی که ممکن است استثناها و خطاهایی ایجاد کند باید در یک بلوک { }try قرار داده شود. در طول اجرا، اگر یک استثنا رخ دهد، کنترل برنامه به اولین بلوکcatch منتقل می شود.
catch block: بلوک catch یک بلوک کنترل کننده استثناست که در آن می توانید برخی از اقدامات مانند logging و رسیدگی به استثنا را انجام دهید. بلوک catch پارامتری از نوع استثنا را می گیرد که با استفاده از آن می توانید جزئیات یک exception را دریافت کنید.
finally block: بلوک finally همیشه اجرا می شود، چه استثنا وجود داشته باشد یا نه. معمولاً، یک بلوک finally باید برای انتشار منابع استفاده شود، به عنوان مثال، برای بستن هر stream یا فایلی که در بلوک try باز شده است.
در قطعه کد زیر اگر یک کاراکتر غیر عددی وارد کنید، ممکن است یک استثنا ایجاد شود.
خروجی قطعه کد بالا در ادامه نشان داده شده است.
برای کنترل استثناهای احتمالی در مثال بالا، کد را در داخل یک بلوک try قرار دهید و مانند شکل زیر، استثنا را در بلوک catch مدیریت کنید.
خروجی قطعه کد بالا در ادامه نشان داده شده است.
در مثال بالا، این کد را داخل یک بلوک try قرار دادیم. اگر یک استثنا در داخل یک بلوک try رخ دهد، برنامه به بلوک catch می رود. در داخل یک بلوک catch، پیامی را نمایش می دهیم تا کاربر را در مورد اشتباه خود راهنمایی کند و در بلوک finally ، پیغامی را نشان می دهیم که بعد از اجرای برنامه چه کاری انجام دهد.
توجه داشته باشید که بعد از بلوک try باید با catch یا finally یا هر دو بلوک وجود داشته باشد. بلوک try بدون catch یا finally ، یک خطای زمان کامپایل می دهد.
در حالت ایده آل، یک بلوک catch باید شامل پارامتری از یک کلاس استثنای داخلی یا سفارشی باشد تا جزئیات خطا را دریافت کند. آنچه که در زیر می بینید شامل پارامتر نوع Exception است که همه انواع استثناها را می گیرد.
فیلترهای استثنا (Exception Filters)
می توانید از چندین بلوک catch با پارامترهای نوع استثنای مختلف استفاده کنید. که به آنها فیلترهای استثنا یا exception filters می گویند. فیلترهای استثنا زمانی مفید هستند که بخواهید انواع مختلف استثنا را به طرق مختلف مدیریت کنید.
خروجی قطعه کد بالا در ادامه نشان داده شده است.
در مثال بالا، چندین بلوک catch را با انواع مختلف استثنا مشخص کرده ایم. بسته به خطا می توانیم پیام مناسبی را برای کاربر نمایش دهیم تا کاربر همان اشتباه را دوباره تکرار نکند.
توجه داشته باشید که بلوک های catch چندگانه با یک نوع استثنای یکسان مجاز نیستند. یک بلوک catch با نوع Exception پایه باید آخرین بلوک باشد.
بلوک catch نامعتبر (Invalid catch Block)
در زبان برنامه نویسی سی شارپ یک بلوک catch بدون پارامتر و یک بلوک catch با پارامتر Exception در یک دستور try-catch مجاز نیستند، زیرا هر دو یک کار را انجام می دهند.
همچنین، بلوک catch بدون پارامتر { }catch یا بلوک catch عمومی { }catch(Exception ex) باید آخرین بلوک باشد. اگر بلوک های catch دیگری بعد از بلوک { }catch یا catch (Exception ex) وجود داشته باشید، کامپایلر خطا می دهد.
بلوک finally
بلوک finally یک بلوک اختیاری است و باید پس از بلوک try یا catch قرار بگیرد. بلوک finally همیشه اجرا می شود، چه استثنا رخ داده باشد یا نه. بلوک finally معمولاً برای پاکسازی کد استفاده می شود، به عنوان مثال، برای دور انداختن اشیاء مدیریت نشده.
توجه داشته باشید که چندین بلوک finally مجاز نیست. همچنین، بلوک finally نمی تواند کلمات کلیدی return ، continue یا break را داشته باشد.
Nested try-catch
سی شارپ بلوک های try-catch تودرتو یا nested try-catch را قبول می کند. هنگام استفاده از بلوک های try-catch تودرتو، یک استثنا در اولین بلوک catch منطبق که پس از بلوک try در جایی که استثنا رخ داده است، ایجاد می شود.
خروجی قطعه کد بالا در ادامه نشان داده شده است.
یک بلوک catch داخلی در مثال بالا اجرا خواهد شد زیرا این اولین بلوک catch است که همه انواع استثنا را کنترل می کند.
اگر بلوک catch داخلی وجود نداشته باشد که با نوع استثنای ایجاد شده مطابقت داشته باشد، کنترل به بلوک catch خارجی انتقال می یابد تا زمانی که یک فیلتر استثنای مناسب را پیدا کند. مثال زیر را در نظر بگیرید.
خروجی قطعه کد بالا در ادامه نشان داده شده است.
در مثال بالا، استثنایی از نوع DivideByZeroException مطرح خواهد شد. از آنجایی که یک بلوک catch داخلی فقط NullReferenceTypeException را کنترل می کند، این استثنا با یک بلوک catch خارجی کنترل می شود.