با عرض سلام و وقت بخیر خدمت کاربران سایت پی وی لرن ، با دوره کامل آموزش #C در خدمت شما دوستان هستیم. در بخش قبلی به معرفی استثناء ها و کلاس Exception در زبان #C پرداختیم. در این بخش قصد داریم شما را با نحوه مدیریت استثناء ها در #C آشنا کنیم.
اگر یک خطا در برنامه وجود داشته باشد، استثناء هایی توسط CLR یا کد برنامه اجرا می شوند. این استثناء ها باید برای جلوگیری از سقوط (Crash) برنامه مورد استفاده قرار گیرند. در #C برای مدیریت خطاها در برنامه میتوان از بلوک های try, catch & finally استفاده کرد.
1 2 3 4 5 6 7 8 9 10 11 12 | try { // code that may raise exceptions } catch(Exception ex) { // handle exception } finally { // final cleanup code } |
همانطور که در کدهای بالا مشاهده می کنید، کدهایی را در بلوک try قرار دهید که ممکن است یک استثنا را ایجاد کنند و سپس آن را با یک بلوک catch یا finally به پایان برسانید.
بیایید ببینیم چگونه از بلوک try & catch برای رسیدگی به استثنا استفاده کنیم. کد زیر را که می تواند یک استثنا را برگرداند ، در نظر بگیرید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | class Program { static void Main(string[] args) { Console.Write("Enter Student Name: "); string studentName = Console.ReadLine(); IList<String> studentList = FindAllStudentFromDatabase(studentName); Console.WriteLine("Total {0}: {1}", studentName, studentList.Count()); Console.ReadKey(); } private static IList<String> FindAllStudentFromDatabase(string studentName) { var studentList = // find all students with same name from the database return studentList; } } |
در مثال فوق، فرض کنید متد FindAllStudentFromDatabase لیست دانشجویانی که نام آنها توسط کاربر تعیین می شود را از پایگاه داده می گیرد.
در مثال بالا اگر حداقل یک دانش آموز وجود داشته باشد مشکلی پیش نخواهد آمد، در غیر این صورت کامپایلر استثناء NullReferenceException را برمی گرداند . اما شاید نخواهیم برنامه متوقف شود و پیام استثناء به کاربر نمایش داده شود. پس، با استفاده از بلوک try اینکار را انجام میدهیم مانند مثال زیر :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | class Program { static void Main(string[] args) { Console.Write("Enter Student Name: "); string studentName = Console.ReadLine(); try { IList<String> studentList = FindAllStudentFromDatabase(studentName); Console.WriteLine("Total {0}: {1}", studentName, studentList.Count()); } catch(Exception ex) { Console.Write("No Students exists for the specified name."); } Console.ReadKey(); } private static IList<String> FindAllStudentFromDatabase(string studentName) { var studentList = // find all students with same name from the database return studentList; } } |
همانطور که در مثال بالا می بینید، اگر تعداد studentList صفر باشد دستور studentList.Count می تواند یک استثناء را برگرداند. بنابراین این کد را در بلوک try قرار دهید. حال اگر بلوک try به خطا برخورد کند ، برنامه crash نشده و از آن خارج نمی شویم در اینصورت کامپایلر بلوک catch را اجراء کرده و دستورالعمل مناسب را که خودمان تعیین کرده ایم در صورت بروز خطا اجراء میکند.
نکته : بلوک try حتما باید بهمراه بلوک catch یا finally و یا هردو باهم استفاده شود و در غیر اینصورت خطای زمان کامپایل برگشت داده می شود.
اگر کامپایلر در اجرای کدهای درون بلوک try با خطا مواجه شود کدهای در بلوک catch را اجراء می کند پس بلوک catch تنها زمانی اجرا می شود که یک استثناء رخ دهد.
میتوان از چند بلوک catch جداگانه برای مدیریت خطا استفاده کرد، این بلوک های catch چندگانه Exception Filter ها نامیده می شوند. این روش وقتی استفاده می شود که شما می خواهید با بروز خطاهای مختلف از دستورالعمل مربوط به همان خطایی خاص را اجراء کنید.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | class Program { static void Main(string[] args) { Console.Write("Please enter two numbers: "); try { int num1 = int.Parse(Console.ReadLine()); int num2 = int.Parse(Console.ReadLine()); int result = num1 / num2; Console.WriteLine("{0} / {1} = {2}", num1, num2, result); } catch(DivideByZeroException ex) { LogError(ex); Console.Write("Cannot divide by zero. Please try again."); } catch(InvalidOperationException ex) { LogError(ex); Console.Write("Not a valid number. Please try again."); } catch(FormatException ex) { LogError(ex); Console.Write("Not a valid number. Please try again."); } Console.ReadKey(); } |
در مثال بالا، یک بلوک catch چندگانه با انواع استثنائات مختلف را مشخص کردیم، به این ترتیب میتوانیم بسته به نوع خطا که پیام مناسب را به کاربر نشان دهیم تا کاربر یک اشتباه مشابه را دوباره تکرار نکند.
در یک بلوک try – catch نمیتوان از یک بلوک catch بدون پارامتر و یک بلوک catch با پارامتر Exception استفاده کرد، زیرا آنها هر دو یک کار را انجام می دهند.
1 2 3 4 5 6 7 8 9 10 11 12 | try { //code that may raise an exception } catch //cannot have both catch and catch(Exception ex) { Console.WriteLine("Exception occurred"); } catch(Exception ex) //cannot have both catch and catch(Exception ex) { Console.WriteLine("Exception occurred"); } |
برای استفاده از بلوک های catch چندگانه توجه داشته باشید که بلوک catch بدون پارامتر را در آخر قرار دهیم.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | try { //code that may raise an exception } catch { // this catch block must be last block } catch (NullReferenceException nullEx) { Console.WriteLine(nullEx.Message); } catch (InvalidCastException inEx) { Console.WriteLine(inEx.Message); } |
بلوک finally باید پس از بلوک try یا catch قرار بگیرد. بلوک finally همیشه اجراء خواهد شد حتی اگر خطایی رخ ندهد!. بلوک finally به طور کلی برای تمیز کردن کد مورد استفاده قرار می گیرد.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | static void Main(string[] args) { int zero = 0; try { int result = 5/zero; // this will throw an exception } catch(Exception ex) { Console.WriteLine("Inside catch block. Exception: {0}", ex.Message ); } finally { Console.WriteLine("Inside finally block."); } } |
1 2 | Inside catch block. Exception: Attempted to divide by zero. Inside finally |
توجه : بلوک های finally چندگانه مجاز نیستند همچنین، در بلوک finally نمی توان از کلمات کلیدی continue، return یا break استفاده کرد.
#C اجازه می دهد بلوک های try – catch تودرتو ایجاد کنید . مانند مثال زیر :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | static void Main(string[] args) { Student std = null; try { try { std.StudentName = ""; } catch { Console.WriteLine("Inner catch"); } } catch { Console.WriteLine("Outer catch"); } } |
خروجی :
1 | Inner catch |
اگر هیچ بلوک catch درونی با نوع استثنای مناسب وجود نداشته باشد، پس از آن استثناء به بلوک catch خارجی وارد می شود تا زمانی که فیلتر مناسب خطا آن را مدیریت کند . مثال زیر را در نظر بگیرید:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | static void Main(string[] args) { Student std = null; try { try { // following throws NullReferenceException std.StudentName = ""; } catch (InvalidOperationException innerEx) { Console.WriteLine("Inner catch"); } } catch { Console.WriteLine("Outer catch"); } } |
خروجی :
1 | Outer catch |
در مثال بالا استثناء NullReferenceException برگشت داده می شود ، اما هیچ بلوک catch ای این نوع خطا را مدیریت نمی کند بنابراین خطا به catch بیرونی انتقال پیدا می کند و در ادامه این خطا در بلوک catch کلی مدیریت می شود.
در این بخش با نحوه مدیریت خطاها با استفاده از بلوک try – catch – finally آشنا شدید.
در قسمت بعد قصد داریم نحوه مدیریت دقیق تر و دستی خطاها را با استفاده از کلمه کلیدی throw بیان کنیم.