در فصل قبل مشاهده کردید که در زمان اجرای برنامه ممکن است خطاهایی رخ دهند. این خطا ها به صورت استثنا در برنامه رخ میدهد. در سی شارپ ار دستوراتی برای مدیریت Exceptionها استفاده میکنیم. در این فصل با این دستورات آشنا میشویم.
مدیریت Exception ها
در فصل قبل مشاهده کردید که اگر خطایی در برنامه به وجود آید یک exception به وسیله CLR و یا کد برنامه ایجاد می شود. exception تولید شده برای جلوگیری از شکست برنامه باید مدیریت شود.
سی شارپ شامل دستوراتی است که با استفاده از آنها می توان exception ها را مدیریت کرد. سی شارپ از بلاکهای catch
، try
و finally
برای این منظور استفاده می کند :
try
{
// code that may raise exceptions
}
catch(Exception ex)
{
// handle exception
}
finally
{
// final cleanup code
}
همانطور که در قاعده نوشتاری بالا مشاهده می کنید باید کدی را که ممکن است باعث بروز خطا شود در بلاک try
قرار دهیم و به دنبال آن از بلاک های catch
و یا finally
استفاده کنیم. بعد از رخداد استثنا در بلاک try کدهای بلاک catch برای مدیریت آن استثنا اجرا می شوند.
بیایید از try و catch در یک نمونه مثال واقعی استفاده کنیم. به نمونه مثال زیر که باعث ایجاد یک exception میشود دقت کنید :
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;
}
}
نمونه کد بالا قرار است نام دانش آموزی را از کاربر دریافت کرده و همه دانش آموزانی با آن نام را از دیتابیس واکشی کند. کد بالا برای زمانی که جستجو در دیتابیس حداقل شامل یک دانش آموز باشد به خوبی کار می کند. اما در صورتی که دانش آموزی پیدا نشود استثنای NullReferenceException
روی می دهد.
ما نمی خواهیم پیغام خطای exception ، مستقیم به کاربر نشان داده شود. بنابراین آن را با بلاک های try و catch مدیریت میکنیم :
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 دارای مقدار null باشد، ()studentList.Count
باعث رخداد یک استثنا می شود. به همین دلیل آن را داخل یک بلاک try قرار میدهیم.
نکته : به دنبال بلاک try باید از بلاک های catch یا finally و یا هر دو استفاده نمود. استفاده از بلاک try به تنهایی باعث به وجود آمدن خطای کامپایلری میشود.
بلاک Catch
استثناهایی که داخل بلاک try
تولید میشود به وسیله کد های بلاک catch
مدیریت می شود. کدهایی که در بلاک catch قرار می گیرند تنها زمانی اجرا میشوند که یک استثنا رخ دهد. در صورتی که نوع استثنا برای ما مهم باشد میتوان از چندین بلوک catch به دنبال بلاک try استفاده کرد. در این صورت بسته به نوع استثنا ، کد مربوط به بلاک catch مورد نظر اجرا میشود :
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
استفاده می کنید نمی توانید از Exception
یکسان برای آنها استفاده کنید.
زمانی که از چندین بلاک catch
استفاده می کنید ، نمیتوان از بلاک catch بدون پارامتر و بلاکی که دارای پارامتر Exception
است، به طور همزمان استفاده کرد. زیرا که هر دوی آنها یکی هستند :
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 که شامل استثنای Exception است، باید همیشه آخرین بلاک باشد. نمونه مثال زیر با خطا روبرو می شود :
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
بلاک finally
همیشه بعد از بلاک try و یا catch میآید. کد های بدنه ی finally
چه استثنا رخ دهد و چه رخ ندهد همیشه اجرا خواهند شد :
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.");
}
}
خروجی نمونه مثال بالا به شکل زیر است :
Inside catch block. Exception: Attempted to divide by zero. Inside finally
نکته : نمیتوان از چندین بلاک finally
استفاده نمود. همچنین بلاک finally نمیتواند شامل دستورات return ، continue و break باشد
try-catch تو در تو
سی شارپ اجازه میدهد دستورات try-catch را به صورت تو در تو به کار ببریم.
static void Main(string[] args)
{
Student std = null;
try
{
try
{
std.StudentName = "";
}
catch
{
Console.WriteLine("Inner catch");
}
}
catch
{
Console.WriteLine("Outer catch");
}
}
خروجی نمونه مثال بالا به شکل زیر است :
Inner catch