فصل ۶ — عملگرها (Operators)
عملگرها در برنامهٔ شما میان دو عدد یا عبارت عددی، عملیات ریاضی، مقایسهای یا منطقی انجام میدهند. یک نمونهٔ سادهٔ عملگر، علامت جمع + یا تفریق - است. اگر در Access از فرمولها استفاده کرده باشید، پیشتر با بسیاری از عملگرها روبهرو شدهاید.
عملگرها دارای ترتیب تقدم (Order of Precedence) هستند و این ترتیب مشخص میکند محاسبات به چه ترتیبی انجام شوند. در هر یک از گروههای حسابی، مقایسهای و منطقی، ارزیابی عملگرها از بالا به پایین مطابق جدول زیر انجام میشود.
| حسابی | مقایسهای | منطقی |
توان ^ | برابری = | Not |
منفیسازی - | نابرابری <> | And |
ضرب و تقسیم * و / | کوچکتر از < | Or |
تقسیم صحیح \ | بزرگتر از > | Xor |
باقیمانده Mod | کوچکتر یا مساوی <= | Eqv |
جمع و تفریق + و - | بزرگتر یا مساوی >= | Imp |
الحاق رشته & | Like / Is | |
با پرانتز میتوان این ترتیب تقدم را تغییر داد. عبارتهای داخل داخلیترین جفت پرانتز همیشه ابتدا ارزیابی میشوند. استفاده از پرانتز برای تغییر ترتیب تقدم میتواند نتیجهای متفاوت از انتظار ایجاد کند، بنابراین لازم است نحوهٔ کار آن را دقیق بدانید. مثالهای زیر را در یک Subroutine داخل یک Module امتحان کنید:
MsgBox (10 + 6) / 3
نتیجه 5.3333 است.
MsgBox 10 + 6 / 3
نتیجه 12 است. در مثال اول پرانتز باعث میشود 10 + 6 پیش از تقسیم بر ۳ محاسبه شود. در مثال دوم، تقسیم 6 / 3 تقدم دارد و پس از آن ۱۰ اضافه میشود.
عملگرهای حسابی
عملگرهای جمع +، تفریق -، ضرب * و تقسیم / عملیات حسابی را انجام میدهند.
عملگر *
عملگر * برای ضرب دو عدد استفاده میشود.
MsgBox 6 * 3
نتیجه ۱۸ است. اعداد میتوانند هر عبارت عددی باشند. نوع دادهٔ نتیجه، نوع دقیقترِ عملوندها خواهد بود؛ از Integer با کمترین دقت، سپس Long، Single، Double تا Currency با بیشترین دقت. برای جزئیات بیشتر این انواع داده به فصل ۲ مراجعه کنید. اگر یکی از عملوندها Null باشد، نتیجه نیز Null خواهد بود.
عملگر +
عملگر + برای جمعکردن دو عدد یا دو عبارت به کار میرود.
MsgBox 4 + 2
پاسخ ۶ خواهد بود. این عملگر هم میتواند اعداد را جمع کند و هم رشتهها را به هم بچسباند. الحاق رشته با + ممکن است گیجکننده باشد؛ بنابراین بهتر است از عملگر & استفاده کنید، زیرا همیشه نمیتوان تشخیص داد که با + الحاق رشته رخ خواهد داد یا نه. نمونهٔ انتهای این بخش نشان میدهد استفاده از + چگونه بر الحاق رشته اثر میگذارد.
نوع دادهٔ نتیجه باز هم دقیقترین نوع میان عملوندها است: Integer، Long، Single، Double یا Currency. اگر یک عملوند Null باشد، نتیجه Null است.
قواعد کلی جمع و الحاق عبارتاند از:
- اگر هر دو عملوند عددی باشند، جمع انجام میشود.
- اگر هر دو عملوند رشته باشند، الحاق انجام میشود.
- اگر یک عملوند عددی و دیگری یک
Variant غیر از Null باشد، جمع انجام میشود.
- اگر یک عملوند رشته و دیگری یک
Variant غیر از Null باشد، الحاق انجام میشود.
اگر یک عملوند عددی و دیگری رشته باشد، خطای Type Mismatch رخ میدهد:
MsgBox 1 + " Richard"
توجه کنید اگر برای الحاق از & استفاده کنید این خطا رخ نمیدهد:
MsgBox 1 & " Richard"
عملگر -
عملگر - یک عدد را از عدد دیگر کم میکند یا یک مقدار منفی را نشان میدهد. عبارت زیر پاسخ ۲ میدهد:
MsgBox 6 - 4
و عبارت زیر -5 را نمایش میدهد:
MsgBox -5
عملوندها میتوانند هر عبارت عددی باشند و نوع نتیجه، دقیقترین نوع دادهٔ عملوندها است. وجود Null در یکی از عملوندها، نتیجه را Null میکند.
عملگر /
عملگر / دو عدد را تقسیم میکند و نتیجهای اعشاری (Floating Point) برمیگرداند.
MsgBox 6 / 3
نتیجه ۲ است. اگر باقیماندهای وجود داشته باشد، در قالب رقمهای اعشاری نمایش داده میشود. نوع نتیجه با توجه به دقیقترین عملوند تعیین میشود و در صورت وجود Null، نتیجه Null است.
عملگر \
عملگر \ دو عدد را تقسیم میکند و نتیجهٔ صحیح برمیگرداند.
MsgBox 6 \ 4
پاسخ ۱ است. نوع نتیجه Integer یا Long خواهد بود. اگر یکی از عملوندها Null باشد، نتیجه Null است.
عملگر ^
عملگر ^ یک عدد را به توان یک نما میرساند.
MsgBox 2 ^ 3
پاسخ ۸ است؛ یعنی ۲ به توان ۳. عملوندها میتوانند هر عبارت عددی باشند.
عملگر Mod
عملگر Mod یک عدد را بر عدد دیگر تقسیم میکند و فقط باقیمانده را برمیگرداند.
MsgBox 6 Mod 4
نتیجه ۲ است؛ یعنی باقیماندهٔ تقسیم ۶ بر ۴. این عملگر اغلب برای بررسی زوج یا فرد بودن عدد استفاده میشود. اگر باقیماندهٔ تقسیم بر دو True، یعنی غیرصفر، باشد عدد فرد است.
عملگرهای مقایسهای
عملگرهای مقایسهای دو عبارت را با یکدیگر مقایسه میکنند؛ همانطور که در فصل ۴ و هنگام بحث دربارهٔ تصمیمگیری در VBA دیدید.
MsgBox 3 > 1
چون ۳ بزرگتر از ۱ است، این عبارت True برمیگرداند. عملگرهای مقایسهای همیشه یک مقدار بولی True یا False بازمیگردانند، مگر آنکه Null در مقایسه وجود داشته باشد که در آن صورت نتیجه همیشه Null است.
| عملگر | معنا |
< | کوچکتر از |
<= | کوچکتر یا مساوی |
> | بزرگتر از |
>= | بزرگتر یا مساوی |
= | مساوی |
<> | نامساوی |
اگر هر دو عبارت عددی باشند، مقایسهٔ عددی انجام میشود. اگر هر دو رشته باشند، مقایسهٔ رشتهای انجام میشود. اگر یکی عددی باشد و دیگری رشته، خطای Type Mismatch رخ خواهد داد.
عملگر الحاق
عملگر الحاق & دو عملوند را به یکدیگر متصل میکند.
MsgBox "Richard " & "Shepherd"
نتیجه «Richard Shepherd» است. توجه کنید در پایان "Richard " یک فاصله قرار داده شده تا در رشتهٔ نهایی میان دو واژه فاصله ایجاد شود.
میتوانید اعداد و رشتهها را نیز به هم متصل کنید، اما به خاطر داشته باشید نتیجه یک رشته خواهد بود. مثال زیر «12 Twelve» را میدهد:
MsgBox 12 & " Twelve"
این کار به این دلیل انجام میشود که VBA تشخیص میدهد یک عدد و یک رشته را ترکیب میکنید و عدد ۱۲ را به رشته تبدیل میکند. بااینحال، الحاق دو نوع دادهٔ متفاوت بدون تبدیل آنها به یک نوع یکسان، روش توصیهشدهای نیست. مثال زیر ۳۴ را برمیگرداند، اما ۳۴ به شکل رشته است نه عدد:
MsgBox 3 & 4
عملگرهای منطقی
عملگرهای منطقی روی دو عبارت، عملیات منطقی بیتبهبیت انجام میدهند و برای تعیین نتیجه از محاسبات دودویی استفاده میکنند.
عملگر And
عملگر And بر این مبنا کار میکند که هر دو مقدار باید True، یعنی غیرصفر، باشند. مقدار واقعی True در VBA برابر -1 است. مثال زیر False میدهد، زیرا برای نتیجهٔ کلی True در عملیات And باید هر دو مقدار درست باشند:
MsgBox True And False
میتوان اعداد را نیز با And ترکیب کرد. این کار بر مبنای دودویی انجام میشود. جدول زیر مقدار هر بیت را از بیت ۷ تا بیت ۰ نشان میدهد؛ دو سطر بعدی نمایش دودویی اعداد ۸۴ و ۱۴۵ هستند و سطر آخر نتیجهٔ And آنها را نشان میدهد.
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | n |
| 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 84 |
| 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 145 |
| 0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | 16 |
هر ستون یک بیت از یک عدد هشتبیتی را نشان میدهد. شمارش بیتها از راست به چپ و از بیت ۰ تا بیت ۷ است. ارزش هر بیت توانی از ۲ است؛ بیت ۰ ارزش ۱ و بیت ۷ ارزش ۱۲۸ دارد. در عدد ۸۴، بیتهای ۶، ۴ و ۲ تنظیم شدهاند، زیرا 64 + 16 + 4 = 84. در عدد ۱۴۵ نیز بیتهای ۷، ۴ و ۰ تنظیم شدهاند، زیرا 128 + 16 + 1 = 145.
هنگام انجام And منطقی روی این دو عدد، نتیجه ۱۶ است، چون تنها بیتی که در هر دو عدد مقدار دارد بیت ۴ است:
MsgBox 84 And 145
این محاسبات دودویی غیرمعمول معمولاً برای بررسی تنظیمبودن بیتها در یک عدد یا برای Masking استفاده میشود. Masking مقادیر بعضی بیتها را در یک عدد به True یا False تنظیم میکند. برای نمونه، اگر بخواهید بیت ۷ برابر ۱ شود، عدد هدف را با ۱۲۸، یعنی مقدار بیت ۷، Or میکنید؛ در نتیجه بیت ۷ عدد هدف صرفنظر از مقادیر سایر بیتها به True تنظیم میشود.
بهعنوان نمونهای دیگر، ممکن است متغیری داشته باشید که از هشت بیت برای نگهداری اطلاعات مختلف شبیه مجموعهای از خصوصیات استفاده کند. هر بیت میتواند یک تنظیم مشخص را نمایندگی کند. اگر بیت ۴ نمایندهٔ یک مقدار باشد و بخواهید بدانید تنظیم شده است یا نه، کافی است آن را با ۱۶ And کنید. اگر بیت تنظیم شده باشد نتیجه ۱۶ و در غیر این صورت ۰ خواهد بود. این بررسی کاملاً مستقل از مقادیر سایر بیتهاست.
عملگر Not
عملگر Not یک نفی منطقی انجام میدهد و عملاً بیتهای یک عدد را معکوس میکند. اگر بیتی ۰ باشد به ۱ و اگر ۱ باشد به ۰ تبدیل میشود.
MsgBox Not (2 = 3)
نتیجه True است، چون 2 = 3 نادرست است و Not آن را معکوس میکند.
عملگر Or
در Or یا هر دو مقدار میتوانند True باشند، یا یکی True و دیگری False. بنابراین مثال زیر True برمیگرداند:
MsgBox True Or False
و چون در مثال بعدی هیچ مقدار True وجود ندارد، نتیجه False است:
MsgBox False Or False
در حساب دودویی، 1 Or 1 = 1، 1 Or 0 = 1، 0 Or 1 = 1 و 0 Or 0 = 0.
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | n |
| 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 84 |
| 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 145 |
| 1 | 1 | 0 | 1 | 0 | 1 | 0 | 1 | 213 |
در این مثال، Or منطقی دو عدد نتیجهٔ ۲۱۳، یعنی 128 + 64 + 16 + 4 + 1، را ایجاد میکند:
MsgBox 84 Or 145
عملگر Or اغلب برای Masking در گرافیک و نیز برای ترکیب دو پارامتر استفاده میشود. در فصل ۵، برای ساخت Message Box با آیکن و دکمههای مناسب لازم بود vbExclamation و vbYesNo ترکیب شوند. جمع ساده با + مقدار نادرستی برای Flag لازم میسازد؛ با استفاده از Or نتیجهٔ صحیح حاصل میشود. Or را در عبارتهای If نیز میبینید:
If x = 1 Or y = 1 Then
عملگر Xor
Xor بسیار شبیه Or است، با این تفاوت که True Xor True برابر False میشود. تنها وقتی یکی True و دیگری False باشد نتیجه True است. Xor مخفف Exclusive Or است؛ بنابراین دو مقدار نمیتوانند هر دو همزمان درست یا هر دو نادرست باشند.
MsgBox True Xor False
MsgBox True Xor True
اولی True و دومی False میدهد. در منطق دودویی، 1 Xor 1 = 0، 1 Xor 0 = 1، 0 Xor 1 = 1 و 0 Xor 0 = 0.
| 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 | n |
| 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 84 |
| 1 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 145 |
| 1 | 1 | 0 | 0 | 0 | 1 | 0 | 1 | 197 |
در این محاسبه نتیجه ۱۹۷، یعنی 128 + 64 + 4 + 1 است:
MsgBox 84 Xor 145
نتیجه ویژگی جالبی دارد: اگر نتیجه را دوباره با یکی از اعداد اولیه Xor کنید، عدد دیگر را به دست میآورید:
MsgBox 84 Xor 197
MsgBox 145 Xor 197
عبارت اول ۱۴۵ و عبارت دوم ۸۴ را برمیگرداند. این عملگر اغلب در روشهای سادهٔ رمزگذاری به کار میرود. رشتهای از نویسههای تصادفی در نظر میگیرید و رشتهٔ موردنظر را نویسهبهنویسه با آن Xor میکنید؛ رشتهای ظاهراً تصادفی و بدون الگوی قابل تشخیص تولید میشود. برای رمزگشایی کافی است نتیجه را دوباره نویسهبهنویسه با همان رشتهٔ تصادفی اولیه Xor کنید.
سایر عملگرها
عملگر Is
Is دو متغیر مرجع شیء را مقایسه میکند تا مشخص شود به یک شیء اشاره دارند یا نه. مثال زیر True میدهد چون هر دو عبارت یکساناند:
MsgBox Worksheets(1) Is Worksheets(1)
مثال بعدی False میدهد زیرا دو عبارت یکسان نیستند:
MsgBox Worksheets(1) Is Worksheets(2)
در اینجا Sheet1 با Sheet2 یکسان نیست، چون نام متفاوتی دارد. ممکن است تفاوتهای دیگری هم وجود داشته باشد، اما اگر هر دو Sheet کاملاً جدید باشند نام تنها تفاوت خواهد بود.
عملگر Like
Like دو عبارت رشتهای مشابه را با یک الگو مقایسه میکند. ممکن است چند نویسهٔ نخست آنها یکسان باشد یا تفاوتشان صرفاً در حروف بزرگ و کوچک باشد.
Option Compare Text
Sub test()
MsgBox "RICHARD" Like "richard"
End Sub
اگر دستور Option Compare در بخش Declarations روی Text تنظیم شده باشد، نتیجه True است. اگر روی Binary باشد، نتیجه False خواهد بود. این رفتار مانند پارامتر Compare در تابع Instr فصل ۵ است که تعیین میکند مقایسهٔ Binary یا Text و حساسیت به بزرگی و کوچکی حروف چگونه باشد.
همچنین میتوانید از Wildcardها استفاده کنید. ? نمایندهٔ یک نویسه و * نمایندهٔ یک رشته از نویسههاست؛ درست مانند جستوجوی فایل با نویسههای عام.
| نویسه | معنا |
? | هر یک نویسه |
* | صفر یا چند نویسه |
# | هر رقم منفرد از ۰ تا ۹ |
[charlist] | هر یک نویسهٔ موجود در charlist |
[!charlist] | هر یک نویسهای که در charlist نباشد |
مثالهای زیر True برمیگردانند:
MsgBox "RICHARD" Like "ri?hard"
MsgBox "RICHARD" Like "ric*"
فصل ۷ — اشکالزدایی (Debugging)
در هر کدی که مینویسید ممکن است باگهایی وجود داشته باشند که باعث شکست برنامه، قفلشدن آن یا صرفاً تولید نتایج غیرمنتظره شوند. از دید نویسنده، این حوزه برنامهنویسان واقعاً تحلیلگر را از کسانی جدا میکند که فقط کد تایپ میکنند و امیدوارند کار کند.
گزارشهای زیادی دربارهٔ «خشم رایانهای» نوشته شده است؛ وضعیتی که کاربران یک برنامه از متفاوتبودن نتیجه با انتظارشان ناراحت میشوند. نویسنده میگوید کافی است روی کد برنامهای مانند Access کار کنید تا متوجه شوید خود این برنامه تا چه اندازه سرراست و منظم است. پس از رفع چند باگ در برنامهای که ظاهراً ساده به نظر میرسد، بهتر درک خواهید کرد پشت صحنهٔ یک انتخاب منو یا فشار دکمهٔ OK چه اتفاقی میافتد.
انواع خطاها
هنگام نوشتن کد، خطاها بسیار آسان رخ میدهند. در ادامه انواع خطاهایی که ممکن است ببینید معرفی میشوند.
خطاهای کامپایل (Compile Errors)
این خطاها از کدی ناشی میشوند که ساختار آن نادرست است. ممکن است Property یا Methodای را به کار برده باشید که برای یک Object وجود ندارد، یا یک For بدون Next و یک If بدون End If نوشته باشید.
هنگام اجرای کد، کامپایلر ابتدا کد را بررسی میکند. اگر چنین خطایی پیدا شود، کد اجرا نخواهد شد و پیام خطایی دربارهٔ اولین خطای یافتشده نمایش داده میشود. ممکن است در یک Procedure چند خطای کامپایل وجود داشته باشد، اما فقط نخستین خطا در هر نوبت گزارش میشود. بنابراین ممکن است یک خطا را اصلاح کنید، دوباره اجرا کنید و بلافاصله خطای بعدی ظاهر شود. این وضعیت میتواند آزاردهنده باشد، اما بهترین راه جلوگیری از آن رعایت قواعد کدنویسی از ابتداست. این خطاها هنگام کامپایل ظاهر میشوند و اغلب Design-time یا Compile-time Errors نام دارند.
خطاهای زمان اجرا (Runtime Errors)
خطاهای Runtime زمانی رخ میدهند که برنامه در حال اجراست. برای مثال ممکن است بخواهید فایلی را باز کنید که وجود ندارد یا تقسیم بر صفر انجام دهید. این کارها پیام خطا تولید و اجرای برنامه را متوقف میکنند. در زمان کامپایل مشخص نمیشوند، زیرا قواعد برنامهنویسی را نقض نکردهاند، اما مانع اجرای موفق کد میشوند.
خطاهای منطقی (Logic Errors)
خطای منطقی زمانی رخ میدهد که برنامه آنگونه که انتظار داشتید عمل نمیکند. کد میتواند کاملاً معتبر باشد و بدون هیچ خطای اجرایی اجرا شود، اما نتیجهٔ کار غلط باشد. یافتن این خطاها از همه دشوارتر است و حتی با همهٔ ابزارهای Debug ممکن است به جستوجوی بسیار دقیق نیاز داشته باشد. گاهی چند خط را بارها میبینید و فکر میکنید «هیچ مشکلی ندارد و باید پاسخ درست را بدهد»، اما فقط با بررسی خطبهخط متوجه اشتباه، که گاهی بسیار ساده است، میشوید. همچنین ممکن است برنامه به شرایطی برسد که هنگام طراحی اولیه پیشبینی نکرده بودید.
Design Time، Runtime و Break Mode
هنگام کار با یک برنامهٔ VBA میتوانید در سه حالت باشید:
- Design time: زمانی که روی کد برنامه کار میکنید یا یک فرم را طراحی میکنید.
- Runtime: زمانی که کد یا فرم در حال اجراست. در Title Bar محیط VBA واژهٔ
running دیده میشود. در این حالت میتوانید کد را ببینید اما نمیتوانید آن را تغییر دهید.
- Break: اگر در زمان اجرا
CTRL+BREAK را فشار دهید، اجرای کد متوقف میشود. همچنین میتوانید با کلید F9 یک Breakpoint قرار دهید و با فشردن دوبارهٔ آن Breakpoint را حذف کنید. از منوی VBE نیز مسیر Debug | Toggle Breakpoint همین کار را انجام میدهد. کادر خطایی با پیام «Code execution has been interrupted» ظاهر میشود و با کلیک روی دکمهٔ Debug وارد پنجرهٔ کد میشوید.
با کلیک روی Debug وارد Instant Watch Mode، که Debug Mode نیز نامیده میشود، خواهید شد. خطی که اجرا در آن متوقف شده با رنگ زرد مشخص میشود. میتوانید نشانگر ماوس را روی هر متغیری که در Scope همان Procedure است قرار دهید تا مقدار آن بلافاصله دیده شود. همچنین میتوانید با کشیدن فلش زرد به خط دیگری، نقطهٔ اجرای بعدی را جابهجا کنید.
برنامهٔ سادهٔ زیر را امتحان کنید:
Sub TestDebug()
x = 2
Do Until x = 1
x = x + 1
Loop
End Sub
این برنامه هرگز پایان نمییابد، زیرا x هیچوقت برابر ۱ نخواهد شد. CTRL+BREAK را بزنید؛ پنجرهٔ خطا ظاهر میشود. روی Debug کلیک کنید تا وارد Instant Watch Mode شوید. نشانگر را روی هر نمونهٔ x ببرید تا مقدار آن ظاهر شود.
شکل ۷-۱ — نمونهای از Instant Watch
میتوانید با کلیک روی نماد Run در نوار ابزار، یعنی مثلثی که به سمت راست اشاره میکند، یا با F5 اجرای کد را از همان نقطهای که متوقف شده بود ادامه دهید. در سمت چپ کد یک فلش زرد نقطهٔ جاری اجرا را نشان میدهد. فلش زرد را به نقطهٔ شروع جدیدی مانند x = 2 بکشید و کد را دوباره اجرا کنید. این روش زمانی مفید است که پس از تغییر کد برای رفع باگ بخواهید یک حلقه یا شرط If را دوباره اجرا کنید.
Breakpointها
میتوانید Breakpointای به کد اضافه کنید تا اجرا در همان خط متوقف شود. سپس با فشار F8 خطبهخط پیش بروید و با Instant Watch متغیرها را هنگام اجرای هر خط بررسی کنید.
Breakpoint با F9 یا گزینهٔ Debug | Toggle Breakpoint روشن و خاموش میشود و در سمت چپ پنجرهٔ کد به شکل دایرهٔ قهوهای پُر دیده میشود. اگر مثال حلقهٔ بالا را اجرا کنید و روی دستور Loop Breakpoint بگذارید، هر بار که Run یا F5 را بزنید اجرا روی همان Loop متوقف میشود.
برای حرکت یک خط در هر مرحله میتوانید F8 یا Debug | Step Into را انتخاب کنید. گزینههای دیگر شامل Step Over (SHIFT+F8)، Step Out برای ادامهٔ عادی برنامه و Run to Cursor برای اجرای کد تا محل نشانگر هستند. حین Step کردن همچنان میتوانید مقادیر را با قرار دادن نشانگر روی متغیرها بررسی کنید. همچنین میتوانید Debug Window را برای Watch کردن Expressionها باز کنید.
استفاده از دستور Stop
نوشتن دستور Stop در کد مانند قراردادن Breakpoint است، با این تفاوت که خود دستور بخشی از کد است. VBA با رسیدن به Stop اجرا را متوقف و به Break Mode میرود. هرچند Stop شبیه Breakpoint عمل میکند، روش تنظیم و پاککردن آن متفاوت است.
Breakpointهای ایجادشده با F9 پس از خروج از پروژه و بارگذاری دوباره پاک میشوند. اما دستورهای Stop جزئی از کدند و فقط با حذف آنها یا قراردادن یک تکنقلقول ' در ابتدای خط و تبدیل آن به Comment غیرفعال میشوند. پس از اطمینان از کارکرد درست کد، همهٔ دستورهای Stop را حذف کنید.
اجرای بخشهای انتخابی کد
اگر دقیقاً میدانید کدام دستور خطا ایجاد میکند، یک Breakpoint مشکل را مشخص میکند. اما معمولاً فقط ناحیهٔ تقریبی کد مشکلدار را میدانید. میتوانید در نقطهای که میخواهید بررسی را آغاز کنید Breakpoint بگذارید و سپس Procedure را خطبهخط اجرا کنید تا رفتار هر دستور را ببینید. همچنین میتوانید یک دستور را رد کنید یا اجرا را از نقطهای دیگر آغاز کنید.
Single Stepping
Single Stepping امکان اجرای یک دستور در هر مرحله را میدهد. در هر جای کد میتوانید نشانگر را روی متغیرها قرار دهید و وضعیت آنها را ببینید یا از Debug Window برای مشاهدهٔ مقدارها استفاده کنید.
برای Single Step از Debug | Step Into یا F8 استفاده کنید. برای اجرای کد تا محل نشانگر، روی یک خط اجرایی کلیک کنید و CTRL+F8 را بزنید. کد فقط تا همان محل اجرا میشود. نشانگر باید روی یک خط قابل اجرا باشد، نه خط خالی. اگر چند دستور در یک خط با : از هم جدا شده باشند، میتوانید میان همان دستورها نیز Step کنید:
temp = 4: If temp = 3 Then Exit Sub
Step کردن Procedureها
اگر Subroutine یا Function دیگری فراخوانی میشود، ممکن است نخواهید تکتک خطوط آن را دنبال کنید؛ شاید آن بخش قبلاً آزمایش شده و از عملکردش مطمئن باشید. اگر با F8 حرکت کنید وارد تمام خطوط Subroutine میشوید و این کار میتواند وقتگیر باشد. با SHIFT+F8 یا Step Over، آن Subroutine مانند یک دستور واحد اجرا میشود و وارد خطوط داخلی آن نمیشوید.
کادر Call Stack
کادر Call Stack فهرستی از فراخوانیهای فعال Procedure را نشان میدهد؛ یعنی فراخوانیهایی که آغاز شده ولی هنوز کامل نشدهاند. این کادر با CTRL+L نمایش داده میشود و فقط در Break Mode در دسترس است. Call Stack بهویژه هنگام فراخوانیهای تودرتو، که یک Procedure دیگری را فراخوانی میکند، برای ردیابی مسیر اجرا مفید است.
شکل ۷-۲ — فراخوانی یک Subroutine و نمایش Procedureای که آن را فراخوانده است
قدیمیترین فراخوانی فعال در بالای فهرست قرار میگیرد و فراخوانیهای بعدی در بالای آن افزوده میشوند. با انتخاب Procedure و کلیک روی Show میتوانید دستوری را ببینید که آن Procedure را فراخوانی کرده است؛ یک فلش سبز این محل را نشان میدهد.
پنجرهٔ Debug
Debug Window به شما اجازه میدهد روی متغیرها یا Propertyهای مشخص Watch قرار دهید و مقادیر آنها را در زمان اجرای برنامه ببینید. با این کار میتوانید متغیر یا Property دارای مقدار نادرست را تحلیل و منبع مشکل را پیدا کنید.
برای ایجاد Watch از مسیر Debug | Add Watch استفاده کنید.
شکل ۷-۳ — افزودن Watch روی یک متغیر
متغیر یا عبارتی را که میخواهید پایش کنید در کادر Expression وارد کنید. برای مثال، اگر متغیر x دارید، x را وارد کنید. جزئیات Context بهطور خودکار وارد میشوند، اما قابل تغییرند. همچنین میتوانید تعیین کنید کد زمانی متوقف شود که مقدار متغیر True، یعنی غیرصفر، شود یا زمانی که مقدار آن تغییر کند.
با کلیک روی OK، Debug Window با جزئیات متغیر ظاهر میشود. وقتی برنامه به Breakpoint برسد، مقدار Watch بهروزرسانی میشود و حین Step کردن نیز مرتب تغییر میکند. برای حذف یک Watch Expression آن را در Watch Window انتخاب و کلید DELETE را فشار دهید. برای ویرایش آن، روی عبارت انتخابشده راستکلیک کنید و Edit Watch را برگزینید.
شکل ۷-۴ — پایش مقدار یک متغیر در Watch Window
رویدادهایی که هنگام اشکالزدایی مشکل ایجاد میکنند
برخی Eventهای پنجره میتوانند فرایند Debug را پیچیده و گیجکننده کنند.
Mouse Down
Mouse Down زمانی رخ میدهد که کاربر یکی از دکمههای ماوس را پایین فشار میدهد، پیش از آنکه دکمه دوباره بالا بیاید. اگر در همین نقطه کد را متوقف کنید، Event مربوط به Mouse Up را دریافت نخواهید کرد. Mouse Up فقط زمانی رخ میدهد که دکمهٔ ماوس فشرده و سپس رها شود.
Key Down
Key Down زمانی رخ میدهد که کاربر یک کلید صفحهکلید را پایین فشار میدهد، پیش از آنکه کلید دوباره بالا بیاید. اگر کد در این نقطه Break شود، Key Up رخ نمیدهد. Key Up فقط پس از فشردن و رهاکردن کلید اتفاق میافتد.
Got Focus / Lost Focus
رویدادهای Got Focus و Lost Focus زمانی رخ میدهند که کاربر روی فرم یا کنترل خاصی کلیک میکند تا Focus به آن منتقل شود یا از آن خارج شود. اگر کد را در این نقطه متوقف کنید ممکن است نتایج ناسازگاری ببینید. وقوع آن به این بستگی دارد که در لحظهٔ فشردن کلید BREAK، کنترل موردنظر Focus داشته است یا نه.
استفاده از Message Box در اشکالزدایی
علاوه بر روشهای گفتهشده، راههای دیگری هم برای جداکردن باگها وجود دارد. شاید کمی ابتدایی به نظر برسند، اما کار میکنند. Message Boxها همیشه برای فهمیدن اتفاقی که در حال رخدادن است مفیدند؛ میتوانند مقدار یک متغیر یا با الحاق، مقدار چند متغیر را نمایش دهند. وقتی یک Procedure بزرگ دارید و نمیدانید باگ در کدام قسمت است، این روش بسیار کاربردی است.
فرض کنید قطعهکد بزرگی ظاهراً اجرا میشود اما در جایی قفل میکند و حتی به CTRL+BREAK پاسخ نمیدهد؛ شاید تا این لحظه کاملاً درست کار میکرده ولی اکنون به شرایطی رسیده که باعث قفلشدن شده است. پرسش این است که برنامه پیش از وقوع مشکل تا کجای کد پیش رفته است؟ Step کردن کل Procedure زمانبر است. قراردادن Message Box در نقاط راهبردی کد نشان میدهد برنامه تا کجا پیش رفته است. بهتر است پیامها معنادار باشند، مانند Ok1، Ok2 و Ok3. سپس میتوانید ببینید پیش از قفلشدن چه مرحلههایی طی شدهاند. بعد از یافتن باگ، همهٔ Message Boxهای اضافی را پاک کنید.
همچنین میتوانید متغیرها را با هم الحاق کنید:
Sub test_for()
For n = 1 To 4
For m = 2 To 8
MsgBox m & " xxxxx " & n
Next m
Next n
End Sub
این Message Box مقادیر m و n را با ردیفی از حرف x میان آنها نمایش میدهد. هدف xها این است که مقدار Null یا فاصلهٔ خالی پنهان نشود. هرچند در این مثال رخ نمیدهد، گاهی متغیرها ممکن است Null شوند یا، اگر String باشند، فقط فاصله داشته باشند؛ در این صورت شاید فقط یک عدد دیده شود و مشخص نباشد متعلق به m است یا n. ردیف x این دو را از هم متمایز میکند.
اگر هنگام اجرای برنامه نمایش پیوستهٔ مقادیر متغیرها را بخواهید، با کمی ابتکار میتوان آن را ساخت؛ Debug این گزینه را مستقیماً ارائه نمیکند. میتوانید Property مربوط به Caption یک فرم را با کد تغییر دهید. این مثال فرض میکند UserForm1 را از قبل تعریف و در View Mode باز کردهاید. فصل ۹ نحوهٔ ساخت فرم برای استفاده با VBA را توضیح میدهد.
Sub test_for()
For n = 1 To 4
For m = 2 To 8
Forms("UserForm1").Caption = n & " xxxx " & m
Next m
Next n
End Sub
هنگام اجرای برنامه، مقادیر متغیرها در Caption پنجره نمایش داده میشوند. بسته به سرعت تغییر متغیرها و طول Procedure، میتوانید الگوها و آنچه را در هر مرحله زنده رخ میدهد ببینید؛ برای مثال توالی اعداد، صعود و نزول آنها، بیشینهای که به آن میرسند و موارد مشابه. اگر خطای مهمی رخ دهد، میتوانید ببینید در چه مقداری اتفاق افتاده است.
جلوگیری از باگها
طراحی و برنامهریزی دقیق برنامه اهمیت زیادی دارد و به کاهش باگها کمک میکند. اغلب بهتر است برنامه را به بخشهای کوچکتر تقسیم کنید که کدنویسی و آزمایش آنها آسانتر باشد. فکرکردن دربارهٔ یک بخش کوچک کد بسیار سادهتر از تلاش برای حل کل پروژه در یک مرحله است.
کد را مستندسازی و با نویسهٔ تکنقلقول ' Comment کنید؛ Commentها در محیط کد سبز میشوند. اگر برنامه پیچیده باشد، حتی پس از چند روز ممکن است درک دوبارهٔ کاری که کد انجام میدهد دشوار شود. حتی برنامهنویسان حرفهای وقتی پس از چند ماه به کد قبلی برمیگردند گاهی برای فهمیدن منطق آن مشکل دارند. Commentها میتوانند کمک لازم را فراهم کنند. همچنین اگر برنامهنویسی یک سازمان را ترک کند، بدون مستنداتی دربارهٔ هدف کد، ادامهٔ کار برای فرد جدید دشوار خواهد بود.
مهم است بدانید هر متغیر چه چیزی را نمایندگی میکند و هر Function چه کاری انجام میدهد. هرچه برنامه بزرگتر و پیچیدهتر شود، مستندسازی آن نیز مهمتر میشود؛ بدون مستندات برای بهخاطر سپردن معنای تمام متغیرها به حافظهای بسیار خوب نیاز خواهید داشت.