درس پانزدهم - برخورد با استثناها (Exception Handling)

 
ارسال شده در تاریخ 1394/5/21 توسط admin در زمینه آموزش سی شارپ
 
آموزش زبانهای برنامه نویسی کامپیوتری

 در این درس با چگونگی برخورد با استثناها یا خطاهای غیر قابل پیش بینی در زبان برنامه سازی C# آشنا می شویم اهداف ما در این درس بشرح زیر میباشد :

(1 درک و فهم صحیح یک استثناء یا Exception
(2 پیا دهسازی یک روتین برای برخورد با استثناها بوسیله بلوک try/catch (3 آزادسازی منابع تخصیص داده شده به یک برنامه در یک بلوک finally
 
استثناها، در حقیقت خطاهای غیر منتظره در برنامه های ما هستند اکثراً، میتوان و باید روشهایی را جهت برخورد با خطاهای موجود در برنامه در نظر گرفت و آنها را پیاده سازی کرد بعنوان مثال، بررسی و تایید دا ده های ورودی کاربران، بررسی اشیاء تهی یا Null و یا بررسی نوع بازگشتی متد ها، می توانند از جمله مواردی باشند که باید مورد بررسی قرار گیرن د این خطاها، خطاهایی معمول و رایجی هستند که اکثر برنام ه نویسان از آنها مطلع بوده و راه هایی را برای بررسی آنها در نظر می گیرند تا از وقوع آنها جلوگیری نمایند .
icbc .ir
 
اما زمانهایی وجود دارند که از اتفاق افتادن یک خطا در برنامه بی اطلاع هستید و انتظار وقوع خطا در برنامه را ندارید بعنوان مثال، هرگز نمی توان وقوع یک خطای I/O را پی شبینی نمود و یا کمبود حافظه برای اجرای برنامه و از کار افتادن برنامه به این دلیل .
این موارد بسیار غیر منتظره و ناخواسته هستند، اما در صورت وقوع بهتر است بتوان راهی برای مقابله و برخورد با آنها پیدا کرده و با آنها برخورد نمود در این جاست که مسئله برخورد با استثناها(Exception Handling) مطرح میشود .
 
هنگامیکه استثنایی رخ می دهد، در اصطلاح می گوئیم که این استثناء، thrown شده است در حقیقت thrown ، شیء ای است مشتق شده از کلاس System.Exception که اطلاعاتی در مورد خطا یا استثناء رخ داده را نشان می دهد در قسمتهای مختلف این درس با روش مقابله با استثناها با استفاده از بلوک های try/catch آشنا خواهید شد .
 
کلاس System.Exception حاوی تعداد بسیار زیا دی متد و property است که اطلاعات مهمی در مورد استثناء و خطای رخ داده را در اختیار ما قرار می دهد برای مثال، Message یکی از property های موجود در این کلاس است که اطلاعاتی درباره نوع استثناء رخ داده در اختیار ما قرار می دهد StackTrace . نیز، اطلاعاتی در مورد ) Stack پشته و محل وقوع خطا در Stack در اختیار ما قرار خواهد داد .
icbc .ir
 
تشخیص چنین استثناهایی، دقیقاً با روتی نهای نوشته شده توسط برنامه نویس در ارتباط هستند و بستگی کامل به الگوریتمی دارد
که وی برای چنین شرایطی در نظر گرفته است برای مثال، در صورتیکه با است فاده از متد System.IO.File.OpenRead() ، اقدام به باز کردن فایلی نماییم، احتمال وقوع (Thrown) یکی از استثناهای زیر وجود دارد :
 
SecurityException
ArgumentException
ArgumentNullException
PathTooLongException
DirectoryNotFoundException
UnauthorizedAccessException
FileNotFoundException 
NotSupportedException
 
با نگاهی بر مستندات .Net Framework SDK ، به سادگی می توان از خطاها و استثناهایی که ممکن است یک متد ایجاد کند، مطلع شد تنها کافیست به قسمت Reference/Class Library رفته و مستندات مربوط به Namespace/Class/Methodرا مطالعه نمایید در این مستندات هر خطا دارای لینکی به کلاس تعریف کننده خود است که با استفاده از آن می توان متوجه شد که این استثناء به چه موضوعی مربوط است پس از اینکه از امکان وقوع خطایی در قسمتی از برنامه مطلع شدید، لازم است تا با استفاده از مکانیزمی صحیح به مقابله با آن بپردازید .
icbc.ir
 
هنگامیکه یک استثناء در اصطلاح thrown می شود یا اتفاق می افتد باید بتوان به طریقی با آن مقابله نمود با استفاده از بلوکهای try/catch می توان چنین عملی را انجام داد پیاده سازی این بلوکها بدین شکل هستند که، کدی را که احتمال تولید استثناء در آن وجود دارد را در بلوک try ، و کد مربوط به مقابله با این استثناء رخ داده را در بلوک catch قرار می دهیم در مثال 15-1 چگونگی پی ادهسازی یک بلوک try/catch نشان داده شده است .بدلیل اینکه متد OpenRead() احتمال ایجاد یکی از استثناهای گفته شده در بالا را دارد، آنرا در بلوک try قرار داده ایم 
در صورتیکه این خطا رخ دهد، با آن در بلوک catch مقابله خواهیم کرد در مثال 15-1 در صورت بروز استثناء، پیغامی در مورد استثناء رخ داده و اطلاعاتی در مورد محل وقوع آن در Stack برای کاربر بر روی کنسول نمایش داده میشود .
 
نکته توجه نمایید که کلیه مثالهای موجود در این درس به طور تعمدی دارای خطاهایی هستند تا شما با نحوه مقابله با استثناها آشنا شوید .
 
using System; using System.IO;
 
class TryCatchDemo
{
static void Main(string[] args)
{ try
{
File.OpenRead("NonExistentFile");
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
icbc.ir
 
هر چند کد موجود در مثال 15-1 تنها داری یک بلوک catch است، اما تمامی استثناهایی که ممکن است رخ دهند را نشان داده و مورد بررسی قرار می دهد زیرا از نوع کلاس پایه استثناء، یعنی Exceptionتعریف شده است در کنترل و مقابله با استثناها، باید استثناهای خاص را زودتر از استثناهای کلی مورد بررسی قرار داد کد زیر نحوه استفاده از چند بلوک catch را نشان می دهد:
 
 
catch(FileNotFoundException fnfex)
 
{
Console.WriteLine(fnfex.ToString());
}
 
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
}
 
در این کد، در صورتیکه فایل مورد نظر وجود نداشته باشد، FileNotFoundException رخ داده و توسط اولین بلوک catch مورد بررسی قرار می گیرد اما در صورتیکه PathTooLongException رخ دهد، توسط دومین بلوک catch بررسی خواهد شد علت آنست که برای PathTooLongException بلوک catch ای در نظر گرفته نشده است و تنها گزینه موجود جهت بررسی این استثناء بلوک کلی Exception است .نکته ای که در اینجا باید بدان توجه نمود آنست که هرچه بلوکهای catch مورد استفاده خاص تر و جزئی تر باشند، پیغامها و اطلاعات مفیدتری در مورد خطا میتوان بدست آورد .
 
استثناهایی که مورد بررسی قرار نگیرند، در بالای Stack نگهداری می شوند تا زمانیکه بلوک try/catch مناسبی مربوط به آنها یافت شود در صورتیکه برای استثناء رخ داده بلوک try/catch در نظر گرفته نشده باشد، برنامه متوقف شده و پیغام خطایی ظاهرمی گردد این چنین حالتی بسیار نا مناسب بوده و کاربران را دچار آشفتگی خواهد کرد استفاده از روشهای مقا بله با استثناها در برنامه، روشی مناسب و رایج است و باعث قدرتمند تر شدن برنامه می شود .
icbc.ir
 
یکی از حالتهای بسیار خطرناک و نامناسب در زمان وقوع استثناها، هنگامی است که استثناء یا خطای رخ داده باعث از کار افتادن برنامه شود ولی منابع تخصیص داده شده به آن برنامه آ زاد نشده باشند .هر چند بلوک catch برای برخورد با استثناها مناسب است ولی در مورد گفته شده نمی تواند کمکی به حل مشکل نمای د برای چنین شرایطی که نیاز به آزادسازی منابع تخصیص داده شده به یک برنامه داریم، از بلوک finally استفاده میکنیم .
 
کد نشان داده شده در مثال 15-2 ، به خوبی روش استفاده از بلوک finally را نشان می دهد همانطور که حتماً می دانید، رشته های فایلی پس از اینکه کار با آنها به اتمام می رسد باید بسته شوند، در غیر اینصورت هیچ برنامه دیگری قادر به استفاده از آنها نخواهد بود در این حالت، رشته فایلی، من بعی است که می خواهیم پس از باز شدن و اتمام کار، بسته شده و به سیستم باز گردد در مثا ل 15-2، outStream با موفقیت باز می شود، بدین معنا که برنامه handle ای به یک فایل باز شده در اختیار دارد
icbc.ir
اما زمانیکه می خواهیم inStraem را باز کنیم، استثناء FileNotFound رخ داده و باعث می شود که کنترل برنامه سریعاً به بلوک catch منتقل گردد .
 
در بلوک catch می توانیم فایل outStream را ببندی م اما برنامه تنها زمانی به بلوک catch وارد می شود که استثنایی رخ ده د پس اگر هیچ استثنائی رخ نداده و برنامه به درستی عمل نماید، فایل باز شده outStream هرگز بسته نشده و یکی از منابع سیستم به آن بازگردانده نمی شود بنابراین باید برای بستن این فایل نیز فکری کرد این کاری است که در بلوک finally رخ می دهد بدین معنا که در هر حالت، چه برنامه با استثنائی روبرو شود و چه نشود، قبل از خروج از برنامه فایل باز شده، بسته خواهد شد در حقیقت می توان گفت بلوک finally ، بلوکی است که تضمین می نماید در هر شرایطی اجرا خواهد شد پس برای حصول اطمینان از اینکه منابع مورد استفاده برنامه پس از خروج برنامه، به سیستم باز گردانده میشوند، میتوان از این بلوک استفاده کرد .

 
using System;
 
using System.IO;
 
class FinallyDemo
{
static void Main(string[] args)
{
FileStream outStream = null; FileStream inStream = null; try
{
outStream = File.OpenWrite("DestinationFile.txt"); inStream = File.OpenRead("BogusInputFile.txt");
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString());
} finally
{
if (outStream != null)
{
outStream.Close();
Console.WriteLine("outStream closed.");
}
if (inStream != null)
{
inStream.Close();
Console.WriteLine("inStream closed.");
}
}
}
}
icbc .ir
 
استفاده از بلوک finally الزامی نیست، اما روشی مناسب برای بالا بردن کارآیی برنامه است ممکن است سوالی در اینجا مطرح شود در صورتیکه پس از بلوک catch و بدون استفاده از بلوک finally ، فایل باز شده را ببندیم، باز هم منبع تخصیص داده شده به برنامه آزاد می شود پس چه دلیلی برای استفاده از بلوک finally وجود دارد؟ در پاسخ به این سوال باید گفت، در شرایط نرمال که تمامی برنامه بطور طبیعی اجرا می شود و اتفاق خاصی رخ نمی دهد، می توان گفت که دستورات بعد از بلوک catch اجرا شده و منبع تخصیص داده شده به سیستم آزاد می شود اما برای بررس ی همیشه باید بدترین حالت را در نظر گرفت فرض کنید درون خود بلوک catchاستثنائی رخ دهد که شما آنرا پیش بینی نکرده اید و یا این استثناء باعت متوقف شدن برنامه شود، در چنین حالتی کدهای موجود بعد از بلوک catch هرگر اجرا نخواهند شد و فایل همچنان باز می ماند .اما با استفاده از بلوک finally می توان مطمئن بود که کد موجود در این بلوک حتماً اجرا شده و منبع تخصیص داده شده به برنامه آزاد می گردد .
 
در اینجا به پایان درس پانزدهم رسیدی م هم اکنون می بایست درک صحیحی از استثناء بدست آورده باش ید همچنین می توانید به سادگی الگوریتمهایی جهت بررسی استثناها بوسیله بلوکهایtry/catch پیاده سازی نمایید بعلاوه می توانید با ساتفاده از بلوک finally مطمئن باشید که که منابع تخصیص داده شده به برنامه، به سیستم باز خواهند گشت چراکه این بلوک حتما اجرا میشود و می توان کدهای مهمی را که می خواهیم تحت هر شرایطی اجرا شوند را درون آن قرار داد .

 

Copyright © 2014 icbc.ir