آموزش سی شارپ – مدیریت خطا
در این بخش بررسی مبحث پیشرفته و تکمیلی این زبان با عنوان Exception Handling را داریم.
حتما قبل از بررسی مدیریت خطا نوشتهی مطالب قبلی این دورهی آموزشی را با هشتگ #دوره آموزشی_سی_شارپ در سایت ما را مطالعه کنید.
آموزش سی شارپ مدیریت خطا
تعریف Exception Handling
exception خطایی است که در runtime (زمان اجرا) اتفاق میافتد. با استفاده از زیرسیستم exception-handling در سیشارپ، شما میتوانید از یک روش کنترل شده و سازمانیافته، خطاهای runtime را handle کنید. یکی از مزیتهای اصلی exception handling این است که بهطور خودکار خطاگیری را انجام میدهد. Exception handling یک بلاک کد (exception handler) تعریف میکند که هنگام بروز خطا بهصورت خودکار اجرا میشود.
کلاس System.Exception
exceptionها توسط کلاسها ارائه میشوند. همهی کلاسهای exception باید از کلاس Exception مشتق شوند که خودش بخشی از System namespace است.
اصول Exception Handling
handling در سیشارپ توسط چهار کلمهکلیدی try، catch، throw و finally مدیریت میشود. که یک زیرسیستم مرتبط را بهوجود میآورند که استفاده از هرکدام، اشاره به استفاده از دیگری دارد. آن قسمت از خط کدهای برنامه که قصد دارید خطاهای (exceptions) آن را بررسی کنید، درون block try قرار میگیرند. اگر یک exception درون try block رخ دهد، این exception (به اصطلاح) پرتاب (throw) میشود. کد شما میتواند این exception را در قسمت block catch دریافت و به روشی منطقی آن را handle کند. Exceptionهای استاندارد سیستم، خودشان بهصورت خودکار throw میشوند. اما برای throwکردن یک exception بهصورت دستی باید از کلمهکلیدی throw استفاده کنید. هر کدی که در نهایت تحت هر شرایطی باید اجرا شود در قسمت finally block قرار میگیرد.
نحوه استفاده از Exception Handling
try
{}
catch (ExcepType1 exOb)
{
}
catch (ExcepType2 exOb)
{
}
ExcepType نوع exceptionای هست که رخ داده است. هنگامی که یک exception پرتاب میشود، توسط جزء catch مرتبط با خودش گرفته شده است. سپس exception در آن قسمت با یک روش منطقی handle میشود.
مثال:
try
{
Console.WriteLine("Before exception is generated.");
for (int i = 0; i < 10; i++)
{
nums[i] = i;
Console.WriteLine("nums[{0}]: {1}", i, nums[i]);
}
Console.WriteLine("this won't be displayed.");
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("Index out-of-bounds!");
}
نوع exception پرتاب شده باید با نوع exception مشخص شده در catch تطابق پیدا کند. برای مثال در برنامهی زیر یک exception از نوع IndexOutOfRangeException پرتاب میشود. اما در قسمت catch، نوع exception مشخص شده، DivideByZeroException (یکی دیگر از exceptionهای built-in) است. هنگامیکه که exception رخ میدهد، این exception گرفته نشده و برنامه بهشکل غیرعادی پایان مییابد. همچنین میتوان بیشتر از یک catch وابسته به یک try نیاز داریم. اما هر catch باید نوع متفاوتی از exception را بگیرد.
مثال:
int[] numer = { 4, 8, 16, 32, 64, 128, 256, 512 };
int[] denom = { 2, 0, 4, 4, 0, 8 };
for (int i = 0; i < numer.Length; i++)
{
try
{
Console.WriteLine(numer[i] + " / " +
denom[i] + " is " +
numer[i] / denom[i]);
}
catch (DivideByZeroException)
{
Console.WriteLine("Can't divide by Zero!");
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("No matching element found.");
}
}
بعضی وقتها، ممکن است بخواهید تمام exceptionها را بدون در نظر گرفتن نوع آنها، بگیرید. برای انجام اینکار، یک مدل catch مشخص میکنید که exception type و exception variable ندارد.
فرم کلی آن بهشکل زیر است:
catch
{
}
یک try block میتواند درون یک try block دیگر قرار گیرد. در مثال زیر، IndexOutOfRangeException توسط try block داخلی گفته نشده و try block خارجی آن را میگیرد:
int[] numer = { 4, 8, 16, 32, 64, 128, 256, 512 };
int[] denom = { 2, 0, 4, 4, 0, 8 };
try
{
for (int i = 0; i < numer.Length; i++)
{
try
{
Console.WriteLine(numer[i] + " / " +
denom[i] + " is " +
numer[i] / denom[i]);
}
catch (DivideByZeroException)
{
Console.WriteLine("Can't divide by Zero!");
}
}
}
catch (IndexOutOfRangeException)
{
Console.WriteLine("No matching element found.");
Console.WriteLine("Fatal error -- program terminated.");
}
پرتاب کردن یک Exception
میتوان بهصورت دستی یک exception را با استفاده از کلمهکلیدی throw پرتاب کرد.
فرم کلی آن به شکل زیر است:
throw exceptOb;
در اینجا، exceptOb باید یک شیء از کلاس یک exception باشد که از Exception ، ارثبری کرده است.
try
{
Console.WriteLine("Before throw.");
throw new DivideByZeroException();
}
catch (DivideByZeroException)
{
Console.WriteLine("Exception caught.");
}
Console.WriteLine("After try/catch statement.");
پرتاب مجدد یک exception
یک exception گرفته شده توسط یک catch میتواند مجدداً پرتاب شود و این exception سپس میتواند توسط یک outer catch گرفته شود. یکی از مهمترین دلایل پرتاب مجدد یک exception این است. handlerهای بیشتری میتوانند به exception دسترسی پیدا کنند. بهعنوان مثال، ممکن است exception handler اول، یک جنبه از exception و exception handler دوم، جنبهی دیگری از exception بهوجود آمده را handle کند. برای پرتاب مجدد یک exception کافی است فقط به تنهایی از کلمهی throw استفاده کنید.
بهشکل زیر:
Throw;
دقت کنید هنگامیکه یک exception را مجدداً پرتاب میکنید، این exception باید توسط یک outer block گرفته شود.
try
{
int value = 1 / int.Parse("0");
}
catch
{
throw;
}
استفاده از finally
گاهیاوقات شما میخواهید یک بلوک از کد حتماً پس از try/catch اجرا شود. برای مثال، ممکن است یک exception باعث شود تا ادامهی اجرای یک متد پایان یابد اما آن متد یک فایل یا یک network connection را باز کرده است. حتماً باید در نهایت بسته شود. این چنین شرایطی در برنامهنویسی زیاد هستند. سیشارپ راه حل ساده و مناسبی برای آن ارائه داده که این راه حل، استفاده از finally block است. Finally block باید در انتهای دنبالهی catchها قرار بگیرد. فرم کلی try/catch که شامل finally است، بهصورت زیر است:
try
{
}
catch (ExcepType1 exOb)
{
}
catch (ExcepType2 exOb)
{
}...
finally
{
}
Finally block تحت هر شرایطی اجرا میشود. این بدان معناست که مهم نیست try block با موفقیت اجرا شود یا خیر، در نهایت finally block اجرا خواهد شد. لطفا نظرات خود را در ارتباط با “مدیریت خطا در سی شارپ” برای ما بنویسید و در بهتر شدن دورههای بعدی ما را یاری کنید. خب این جلسه هم به پایان رسید باید به این نکته توجه کنید که مباحث بالا در برنامه نویسی تجاری اهمیت فراوانی دارد.
برای استفاده از تمامی جلسات از هشتگ #دوره آموزشی_سی_شارپ در سایت ما استفاده کنید. منتظر جلسهی بعدی دوره آموزشی سی شارپ بمانید.



















استفادهی تو در تو از try-catch برای خطاهای ترکیبی واقعا نکته جالبی بود
خوشحالیم این نوشته برای شما مفید بوده است.
تا حالا نمیدونستم میشه چند تا catch برای یه try نوشت
خوشحالیم این نوشته برای شما مفید بوده است.
اگه فقط از catch بدون مشخص کردن نوع exception استفاده کنیم، همه خطاها گرفته میشن؟
بله، اگر در سیشارپ از یک بلاک catch بدون مشخص کردن نوع استثنا استفاده کنید (یعنی فقط catch { } بنویسید)، این بلاک میتواند همه نوعهای استثناها را بگیرد، چه استثناهای سیستم و چه استثناهای کاربر تعریف شده. این نوع catch معمولاً برای زمانی کاربرد دارد که بخواهید هر خطایی را به شکلی عمومی هندل کنید، اما بهتر است در بیشتر مواقع از catch با نوع مشخص استفاده شود تا کنترل دقیقتری روی خطاها داشته باشید و به اشتباه استثناهای غیرمنتظره یا بحرانی را پنهان نکنید.
استفاده از finally برای بستن منابع مثل فایل یا اتصال شبکه خیلی کاربردیه
دقیقاً، استفاده از بلاک finally برای بستن منابع مثل فایلها یا اتصالهای شبکه بسیار حیاتی است چون تضمین میکند که کد مربوط به آزادسازی منابع حتی در صورت بروز استثناء یا خطا اجرا شود. این باعث میشود منابع سیستم به درستی آزاد شوند و مشکلاتی مثل نشت حافظه یا قفل ماندن فایلها پیش نیاید. با اینکه در سیشارپ معمولاً از ساختار using برای مدیریت خودکار منابع استفاده میکنیم، در مواردی که نیاز به کنترل دقیقتر یا چند منبع متفاوت داریم، بلاک finally گزینهی مناسبی است تا اطمینان حاصل کنیم کد پاکسازی قطعاً اجرا میشود.
فرق throw با throw ex چیه؟
فرق اصلی بین throw و throw ex در سیشارپ در نحوهی انتشار مجدد استثنا و حفظ اطلاعات مربوط به آن است. وقتی فقط از throw استفاده میکنید، استثنا بدون تغییر دوباره پرتاب میشود و Stack Trace (ردپای خطا) حفظ میشود، به این معنی که اطلاعات دقیق محل رخ دادن خطا دستنخورده باقی میماند. اما اگر از throw ex استفاده کنید، استثنا به صورت یک استثنای جدید دوباره پرتاب میشود که باعث میشود Stack Trace قبلی بازنویسی شده و اطلاعات اصلی خطا از دست برود یا حداقل مسیر اصلی خطا به درستی نشان داده نشود. به همین دلیل، برای حفظ اطلاعات دقیق خطا، بهتر است هنگام انتشار مجدد استثنا فقط از throw ساده استفاده کنید.