این مقاله را با خطاهای مدیریت نشده آغاز خواهیم کرد و در ادامه با روش مدیریت خطاهای ایجاد شده با شیءهای پویا آشنا می شویم و درنهایت روش ساخت کلاس سفارشی خطا را خواهیم دید.
(استثناهای اداره نشده) Unhandled Exceptions
اگر به دیاگرام مدیریت خطاها در بخش قبلی مقاله قبل دقت کرده باشید، دیدید که درصورت توانایی نداشتن برنامه در مدیریت خطاها، CLR بهطور خودکار به آن رسیدگی میکند که درنهایت پیغامی مانند شکل۱ را نشان داده و اجرای برنامه خاتمه مییابد اما میتوان این مشکل را بهگونهای بررسی کرد تا حداقل بتوانید اطلاعات مربوط به خطا را ثبت کنید.
ادارهکردن استثناهای مدیریت نشده یکی دیگر از روشهای مدیریت خطاها است در این مرحله بهتر است اطلاعات مربوط به خطای رخ داده در یک برنامه ثبت شود زیرا در پیدا کردن علت خطاها مؤثر خواهد بود. این عمل ثبت معمولاً در فایلهای log برنامه انجام میشود. اما معنی دقیق استثناهای مدیریت نشده چیست؟
بهطور مختصر و کوتاه مکانیزم پردازه خطاهای عادی برنامه زمانی که عملیات در پشته برای پیداکردن یک ادارهکننده یا handler کاملاً انجام شده و هیچ بلوکی که توانایی ادارهکردن خطا را داشته باشد پیدا نمیشود، بلوک catch برای آن خطا تعبیه نشده یا قادر به دریافت آن خطا بهدلیل هماهنگ نبودن نیست، یک خطای مدیریت نشده یا Unhandled Exception اتفاق میافتد. اما همانگونه که بیان شد خطاهای اداره نشده موضوع پیچیدهای نیست و همان فرآیند تولید خطاها است. برای گرفتن استثناهایی که بهصورت Unhandled exceptions رخ میدهند راهکارهایی وجود دارد. نخستین مرحله برای مدیریت این دسته از خطاها مشخصکردن نوع برنامه است هر برنامهای روش خاص خود را برای مدیریت این استثناها دارد اما نکته اصلی این روش ثبت رویداد مربوطه به unhandled Exceptionها در برنامه است.
(در این بخش ساختار دریافت اینگونه استثناها را خواهید دید بدیهی است که هر کدام از روشهای ارائه شده در این بخش ثابت نبوده و قابل بسط دادن هستند.) مواردی که بهصورت پررنگتر مشخص شدهاند، کدهای اصلی برای مدیریت خطاها هستند.
Windows Forms
اگر در برنامههای Windows Forms خطایی رخ دهد که اداره نشده است، پیغامی مانند شکل۲ را دریافت میکنند اما نحوه ادارهکردن استثناها در برنامههای windows forms با فراخوانی دستور SetUnhandledExceptionMode بهعنوان نخستین دستور برنامه انجام میشود. این دستور باید قبل از تعریف هر عنصر UI قرار گیرد. در ادامه رویدادApplication_UnhandledException اضافه میشود. در فهرست ۱ زمانیکه دکمه Test کلیک شود خطای Invalid Operation را تولید میکند که نتیجه آن دریافت خطا در متد Application_UnhandledException است.
در برنامههای کنسول برای ادارهکردن اینگونه استثناها باید از از رویداد system.AppDomain.UnhandledException استفاده کرد. در مرحله نخست ابتدا رویداد مربوطه را به فهرست ۲ تعریف میکنید.
فهرست۳ نمونه کامل شده توضیح بالا را نشان میدهد. در فهرست۲ استثنای InvalidOperationException برای نمونه پرتاب شده است.
WPF
در برنامههای WPF این عمل روی dispatcher انجام میشود(فهرست۴).
ASP.Net در زمان طراحی برنامههای وب مدیریت خطاهای اداره نشده از مواردی است که توجه به آن ضرورت دارد درصورت فراموش کردن این موضوع هنگام بروز چنین خطاهایی نتیجه میتواند بهصورت نمایش یک پیغام خطای مدیریت نشده به کاربر باشد یا بسته به نوع پیکربندی که برای سایت پیادهسازی شده است. جزئیات مربوط به استثنا بههمراه شماره خط بروز خطا و دستوری که آنرا تولید کرده نمایش داده میشود(شکل۳).
مورد دوم را بیشتر در صفحات وبی که طراحان به این نکته توجه نداشتهاند، میتوانید ببینید اما برای مدیریت اینگونه خطاها روشهای مختلفی وجود دارد. اگر بهعنوان یک طراح وب این قسمت را میخوانید، میدانید که یکی از روشهای مدیریت به این صورت است:
1- خطا در سطح رویداد برنامه با استفاده از رویداد Application_error که یکی از رویدادهای کلاس HttpApplication است و در فایل Global.asax قرار دارد، گرفته میشود.
2- خطا پردازش میشود، جزئیات آن گردآوری شده و از طریق ارسال یک ایمیل برای مدیر یا طراح سایت ارسال میشود.
3- کاربر به صفحهای هدایت میشود که در آن پیغامی با این مضمون به نمایش درآمده که خطای رخ داده ثبت شده و برای مدیر سایت ارسال شده است.
از قابلیتهای ارائه شده توسط ASP.Net برای صفحات وب میتوان به تکنیکهای ادارهکردن استثناها اشاره کرد. زمانیکه استثنای مدیریت نشدهای در یک برنامه ASP.Net رخ میدهد، میتوان این خطا را در سطح صفحه که با استفاده از رویداد error انجام میشود یا در سطح برنامه که در این روش نیاز به کلاس سراسری برنامه و رویداد Application_Error که معمولاً در فایل Global.asax از یک برنامه وب قرار دارد دریافت کنید.
روش نخست
مطابق فهرست ۵:
روش دوم
مطابق فهرست ۶:
متدها و خاصیتهای مورد استفاده در Exceptions
exceptionها اشیای مفید و پرکاربردی در محیط .Net بهشمار میروند اما میتوان با استفاده از خاصیتها و متدهایی که برای استثناها در Net.در نظر گرفته شده است جزئیات بیشتری در خصوص خطای تولید شده را دریافت کرد استفاده از آنها در زمان بروز خطاهای پیچیده میتوانند راهگشا باشد این خاصیت ها را در جدول ۱ میبینید.
در قطعه کد فهرست۷ یک خطای تقسیم بر صفر تولید میشود هدف از این قطعه کد نحوه استفاده از خاصیتهای توصیف شده در جدول ۱ در بلوک catch بوده و اطلاعات برگردانده شده توسط هر یک از این خاصیتها را نشان میدهد.
همانگونه که در شکل ۴ میبینید خاصیت Message علت بروز خطا را بیان میکند Inner Exception در اینجا خالی است زیرا یک خطای مستقیم تولید شده TargetSite متدی که خطا در آن بروز کرده را توصیف میکند())HelpLink (Void MyZero در این قسمت خالی میباشد و در نهایت ToString نوع خطا به همراه توصیف آنرا تشریح میکند.
تا این قسمت از بحث با استثناها آشنا شده و نحوه مدیریت آنها در برنامههای مختلف را دیدید و آشنایی کلی با ساختار کلاسها بهدست آوردید. همچنین میدانید .Net دارای یکسری Exceptionهای از پیش طراحی شده است که با استفاده از آنها و با کمک گرفتن از بلوکهای try/catch/finally میتوانید بر خطاها کنترل داشته باشید (مگر در موارد خاص). در کنار این موارد خاصیتهایی که با استفاده از آنها میتوانید اطلاعات کاملتری درباره خطاهای رخ داده شده بهدست آورید نیز نمونهای دیگر از این اطلاعات بودند. حال به نوع دیگری از خطاها میپردازیم که مربوط به نوعهای پویا یا Dynamic هستند و حتی در صورت داشتن یک ترکیب نحوی اشتباه نیز کامپایلر خطایی را گوشزد نمیکند.
Dynamic objects
زمانی که شیئ را با استفاده از کلمه کلیدی dynamic تعریف میکنید، به کامپایلر اعلام میکند نیازی به بررسی متغیر نیست و نیازی به بررسی این نوع از نظر ترکیب نحوی یا عضوهایی که فراخوانی میکند نخواهد بود. در زمان ساخت یک پروژه در محیط سیشارپ در پوشه References مجموعهای از References وجود دارد. Microsoft.CSharp.dll یکی از آنها است که دارای فضای نام RuntimeBinder است و این فضای نام دارای دو کلاس RunTimeBinderException و RuntimeBinderInternalCompilerException است. این دو کلاس برای مدیریت این گونه خطاها بهکار میروند.
RuntimeBinderException
کلاس RuntimeBinderException زمانی که خطایی روی یک نوع پویا رخ دهد را نشان میدهد. یکی از نکات پیراموان اشیای پویا شناسایی نکردن خطاهای احتمالی مانند یک ترکیب نحوی اشتباه توسط کامپایلر است در شرایط معمول کامپایلر اینگونه خطاها را تشخیص داده و به آنها واکنش نشان میدهد. اما در زمان استفاده از اشیای پویا تا زمانیکه برنامه اجرا نشود، این خطاها پنهان خواهند ماند به فهرست ۸ توجه کنید.
خط اول یک شی پویا را مقداردهی اولیه میکند که بهصورت رشته تعریف شده است در خط دوم متدی از کلاس String به نام ToUpper برای بزرگ کردن عبارت فوق استفاده شده است اما خط سوم متدی بهنام toUpPer را استفاده کرده است همانگونه که مشخص است کلاس string دارای چنین متدی با این ترکیب نمیباشد در حالت معمول اگر بهجای استفاده از dynamic از string برای تعریف این فیلد استفاده کنید کامپایلر پیغام خطای (شکل ۵) تولید خواهد کرد.
اما در صورت تعریف بهصورت پویا کامپایلر خطایی تولید نمیکنید و در ادامه اجرای برنامه پیغام خطای (شکل۶) نمایش داده شده و برنامه خاتمه مییابد.
برای برطرفکردن این مشکل تکنیک بلوک try/catch راهگشا خواهد بود. برای این منظور از کلاس RuntimeBinderExcpetion به شکلی که در کد زیر آنرا میبینید، استفاده کرده و قطعه کد قبل را بهصورت فهرست ۹ بازنویسی میکنیم.
حال با نمایش یک پیغام خطا علت بروز خطا را میتوان دریافت کرد. خروجی فهرست ۹ به این صورت خواهد بود(شکل ۷).
طراحی کلاس استثنا
درنهایت، به آخرین بخش این مقاله رسیدیم. در این بخش یکی از تکنیکهای پیشرفته را که برای مدیریت خطاها وجود دارد، خواهید دید. هرچند مورد استفاده از این تکنیک به دلیل گسترده بودن کلاسهای مدیریت خطاها در Net.کم است. اما هنوز احتمال وجود خطایی که برای آن کلاسی تعبیه نشده باشد یا نوعهای ساخته شده توسط کاربر که باعث بروز خطاهایی از نوع تعریفی شوند، وجود دارد.
چهارچوب Net. دارای کلاسهای مختلف استثنا است. اگر استثنایی از قبل در Net. وجود دارد، لازم نیست آنرا بهصورت سفارشی طراحی کنید. همچنین ساخت کلاس استثنا برای همه موارد ضروری نیست (زیرا کار چندان جذابی نیست. اما از طرفی اینگونه کلاسها از انعطافپذیری خوبی برخوردار هستند). بهعنوان نمونه، اگر کاربر مقداری نامعتبر را در فیلدهای ورودی وارد کرده است، نباید برای آن یک کلاسی از این دست ایجاد کرد؛ به دلیل اینکه:
1- یک ورودی نامعتبر یک خطا نیست و با یک پیغام ساده این موضوع را میتوان نشان داد.
2- بهدلیل اینکه تولید یک Exception عملیات را در پشته تا وقتیکه به آن Exception رسیدگی شود، متوقف میکند. در نتیجه کارایی برنامه را پایین میآورد.
اما زمانی که برای یک استثنا کلاسی وجود نداشته باشد یا شیئ را طراحی کردهاید که ویژه برنامهتان است و در صورت بروز خطا باید آنرا handle کنید و در کنار آن اطلاعات اضافهتری را در خصوص بروز آن استثنا به کاربر نشان دهید، در اینصورت است که به ساخت یک کلاس استثنا نیاز پیدا میکنید.