درس سیزدهم : استثناها و ارور هندلینگ – بخش اول
درس سیزدهم : استثناها و ارور هندلینگ – بخش اول (On Error GoTo)
13-1 مقدمه
ممکن است برنامه ای که مینویسیم ورودی هایی را از کاربر دریافت کند. هر برنامه نویسی بارها با این شرایط مواجه میشود. در زبانهایی مانند ویژوال بیسیک و بسیاری زبانهای دیگر موقعی که متغیری برای ذخیره ی داده های ورودی از کاربر تعریف میشود حتما باید نوع داده ای که قرار است در این متغیر ذخیره شود توسط برنامه نویس مشخص شود. داده ی عددی یا رشته ای؟ عدد صحیح یا اعشاری؟ و غیره. این نوع زبانها را اصطلاحا زبانهای Strong Type گوییم. ویژوال بیسیک، و اکثر زبانهای سی استایل از این دسته اند. (زبانهایی مانند پایتون، جاوااسکریپت یا روبی Weak Type هستند. یعنی متغیر ها از جهت نوع داده ای که ذخیره میکنند دسته بندی نمی شوند.) حال اگر بعد از طراحی برنامه کاربر از قصد و یا به اشتباه ورودی ای با نوع داده ی غیر منتظره در برنامه وارد کند چه میشود؟ مثلا اگر قرار است برنامه پیغامی برای دریافت سال تولد کاربر نمایش دهد و بعد آن را از سال کنونی کم کند تا سن کاربر را بدست آورد، درصورتی که کاربر به جای سال تولد خود که یک عدد است، کلمه یا حروفی را وارد کند برنامه باید با این شرایط چه کند؟ در این حالت اگر برنامه نویس این مشکلات را پیش بینی نکرده باشد، برنامه کرش و هنگ میکند. چون برنامه نمیداند با این مشکل پیش آمده چه کند. برای حل این مشکل برنامه نویس باید تمام احتمالات را در نظر گرفته و با استفاده از امکانات ارور هندلینگ زبانهای برنامه سازی برنامه اش را از جهت کرش کردن درصورت اتفاقات غیر منتظره ایمن گرداند. در این درس به معرفی روش On Error GoTo برای ارور هندلینگ می پردازیم.
13-2 روش On Error GoTo
13-2-1 سینتکس On Error GoTo
سینتکس On Error GoTo به صورت زیر است:
On Error GoTo labelName
Statements …
Exit Sub
labelName:
Statements …
در این روش با عبارت کلیدی On Error GoTo قبل از شروع کدی که ممکن است با استثنا مواجه شود، کد نویسی را شروع میکنیم و مقابل آن نام لیبلی که میخواهیم در صورت مواجه شدن برنامه با استثنا کنترل به آن لیبل منتقل شود مقابل عبارت کلیدی On Error GoTo نوشته میشود. سپس به نوشتن باقی کد به صورت معمول میپردازیم. بعد از پایان کد، لیبل را قرار داده و مقابل آن دو نقطه میگذاریم. این یعنی از اینجا به بعد مربوط به این لیبل است. لیبل ها در حقیقت نامهایی دلخواه هستند که با مشخص کردن آنها میتوان برنامه را به بخش های مختلف تقسیم کرد. هرچه زیر این لیبل بنویسید، در صورتی که برنامه به مشکلی برخود کند اجرا خواهد شد. فقط باید توجه کنید که حتما نام این لیبل با لیبلی که مقابل On Error GoTo مینویسید برابر باشد. Exit Sub هم برای این است که اگر برنامه با مشکلی مواجه نشد در Exit Sub خاتمه یابد و دستورات بعد از لیبل را که مخصوص موقع رخداد استثنا هستند اجرا نکند.
13-2-2 مثال از On Error GoTo
همانطور که میدانید برای دریافت ورودی از کاربر باید کدی بنویسیم که آن ورودی در متغیری ذخیره شود. برای تعریف متغیر هم در ویژوال بیسیک باید نوع داده را از ابتدا تعیین کنیم. حال فرض کنید قرار است سال تولد کاربر را دریافت، آن را در متغیری با نوع داده integer یا همان عدد صحیح ذخیره کنیم، بعد کدی بنویسیم که سال جاری را از سال تولد کاربر کم کند و سن او را نمایش دهد. حالا اگر کاربر به هر دلیل در کادری که باید سال تولدش را وارد کند، یک رشته از کلمات و حروف را وارد کند چه میشود؟ اگر برنامه را درحالت دیباگ تست کنید و به جای سال تولد که یک داده ی عددی است، یک داده ی رشته ای وارد کنید، با یک Exception روبرو میشوید (شکل 1-13) این کلمات سعی میکنند در متغیر از پیش تعریف شده ی ما ذخیره شوند. ولی این متغیر نوع داده ی عددی دارد. یعنی فقط عدد میپذیرد و رشته ی حروف و کلمات را نمیتواند بپذیرد. یا از طرفی عملیات ریاضی کسر سال جاری از ورودی کاربر درصورتی امکان پذیر است که ورودی کاربر هم عدد باشد. اگر حروف وارد کرده باشد، عملیات ریاضی بین عدد و کلمه امکان پذیر نیست. در این شرایط چه اتفاقی می افتد؟
شکل 1-13
پاسخ این است که اگر برنامه نویس این مشکلات را پیش بینی نکرده باشد برنامه کرش میکند. برای جلوگیری از چنین مشکلاتی باید از روشهای ارورهندلینگ استفاده کرد تا برنامه به صورت خودکار خطای کاربر را تشخیص داده و با نمایش پیغام مناسب اشتباه کاربر را به او اطلاع دهد. در زیر روش ارور هندلینگ با On Error GoTo را برای سناریوی فوق مشاهده میکنید.
Public Class ageCalc Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click On Error GoTo error_handler1 Dim x As Integer = TextBox1.Text TextBox2.Text = 1395 - x Exit Sub error_handler1: MsgBox("خطا در ورودی!" + vbNewLine + "ورودی را چک کنید و دوباره سعی کنید.", vbOKOnly + vbCritical + vbMsgBoxRtlReading + vbMsgBoxRight, "خطا") TextBox1.ResetText() TextBox2.ResetText() TextBox1.Focus() End Sub End Class
حال به بررسی کد بالا میپردازیم.
در رویداد کلیک چپ دکمه ی button1 از ابتدا با عبارت کلیدی On Error GoTo کد را آغاز کردیم. در مقابل این عبارت کلیدی، لیبلی که میخواهیم خط فرمان اجرا در صورت وقوع ارور به آن لیبل پرش کند را مشخص میکنیم. من آن را error_handler1 نام نهادم.
نامی که برای لیبل انتخاب میکنید به راحتی در سر خط تایپ کرده و یک دونقطه مقابل آن قرار دهید. حالا نوبت به نوشتن ارور هندلر میرسد. میخواهیم وقتی برنامه با ارور مواجه شد، و به این لیبل پرش کرد، یک پیغام خطا نمایش داده شود. با تابع ()MsgBox این پیغام خطا را تعریف میکنیم.
حالا به بالا برمیگردیم. جایی که عبارت On Error GoTo error_handler1 را تایپ کردیم. زیر این عبارت به نوشتن برنامه خود مطابق معمول میپردازیم. یک متغیر از نوع صحیح تعریف کردیم، محتویات textbox1 را به آن نسبت دادیم. سپس محتویات textBox2 را به عنوان خروجی برنامه برابر با حاصل تفریق سال 1395 از متغیر تعریف شده قرار دادیم.
کار تقریبا تمام است. اگر برنامه را همین الان تست کنید، در صورتی که ورودی ها اشتباه باشد با پیغام خطا مواجه میشوید. اما مشکل اینجاست که اگر ورودی ها صحیح باشند هم باز با پیغام خطا مواجه میشوید. به عبارت دیگر هرآنچه برای لیبل error_handler1 نوشتیم، در هر شرایطی اجرا میشود. چه داده ها صحیح باشند و چه غلط باشند. چرا این اتفاق می افتد؟
شکل 2-13 اجرا با ورودی صحیح
به عبارت On Error GoTo error_handler1 توجه کنید این عبارت شرط میکند در صورت مواجه با هرگونه استثنا (exception) یا همان ارور، به لیبل معین جهش کن. یعنی اگر اروری رخ داد برنامه هرچه قبل از لیبل مشخص شده ی ما باشد را رها کرده از روی آنها جهش میکند و به دستورات بعد از لیبل میپردازد. اما اگر اروری رخ نداد، برنامه تمام دستوراتی که نوشته شده به نوبت اجرا میکند. یعنی ابتدا سن را به درستی محاسبه و نمایش میدهد بعد به لیبل میرسد و بعد دستورات زیر آن را که شامل پیغام خطای ما ست هم اجرا میکند و آن پیغام را نیز نمایش میدهد. برای جلوگیری از این اتفاق باید کاری کنیم برنامه در صورت اجرای موفقیت آمیز از ادامه ی اجرا و رسیدن به لیبل error_handler1، باز ماند. توجه کنید که اگر اجرا توانسته به خط قبل از لیبل برسد پس با اروری مواجه نشده، هیچ جهشی انجام نداده و وظیفه ی خود را تمام و کمال انجام داده است. پس خط قبل از نام لیبل محل بسیار مناسبی برای خاتمه ی برنامه است. برای خاتمه ی اجرای برنامه درست قبل از نام لیبل، با عبارت کلیدی Exit Sub اجرای برنامه را خاتمه میدهیم تا در صورتی که برنامه با خطایی مواجه نشده و به درستی اجرا شده باشد، error_handler1 اجرا نشود. حالا اگر برنامه را تست کنید، میبینید که بدون مشکل کار میکند.
شکل 3-13 اجرا با ورودی غلط
تمرین : برنامه ای بنویسید که دو عدد را در هم ضرب کند و اگر اشکالی در ورودی ها وجود داشت، با پیغام مناسب واکنش نشان دهد.