فصل ۲: متغیرها، آرایهها، ثابتها و انواع داده
اگرچه مفهوم داشتن محلهایی برای نگهداری داده در زمانی که برنامه در حال اجراست نسبتاً ساده به نظر میرسد، متغیرها، آرایهها و ثابتها مجموعهای از قواعد نسبتاً پیچیده دارند. این مفاهیم جزء جداییناپذیر هر برنامهاند و به همین دلیل یک فصل کامل به توضیح آنها اختصاص داده شده است.
متغیرها
ساختن ساختاری از متغیرها کمی شبیه ساختن یک بایگانی خانگی است. ممکن است پروندههایی برای شغل، بیمهنامهها، مالیات و مدارک شخصی مانند گذرنامه داشته باشید. اندازهٔ این پروندهها میتواند افزایش یابد. بعضی از آنها فقط یک سند، مثلاً یک بیمهنامه، را نگه میدارند و بعضی دیگر، مانند پروندهٔ مالیاتی، حجم زیادی از اطلاعات را در خود جای میدهند.
نکتهٔ مهم این است که هر پرونده به گروه مشخصی از اسناد اختصاص دارد. برای مثال گذرنامه را در کنار مدارک مالیاتی بایگانی نمیکنید. برای دسترسی سریع و آسان باید بدانید هر نوع مدرک در کدام پرونده قرار دارد.
متغیر را مانند پروندهای در نظر بگیرید که نوع مشخصی از اطلاعات را نگه میدارد. یک متغیر ممکن است تنها یک عدد یا قطعهای متن باشد که برنامه لازم دارد در زمان اجرا آن را حفظ و به آن مراجعه کند. همچنین میتواند یک آرایهٔ کامل از اطلاعات باشد؛ چیزی تقریباً شبیه یک Spreadsheet. یک Spreadsheet سلولهای فراوانی برای نگهداری اطلاعات دارد و یک آرایه نیز میتواند به همان صورت دارای تعداد زیادی سلول یا محل ذخیره باشد.
مقدار یک متغیر در هنگام اجرای برنامه میتواند تغییر کند و به همین دلیل «متغیر» نامیده میشود. همان قواعد مثال بایگانی در اینجا نیز برقرار است: انواع داده را میان متغیرها با یکدیگر مخلوط نمیکنید. اگر متغیری با نوعی مشخص تعریف شده باشد، دادهای از نوع دیگر را نمیپذیرد. برای نمونه، اگر متغیری را بهصورت عدد صحیح تعریف کنید، نمیتوانید متن در آن قرار دهید؛ یا اگر یک عدد اعشاری را در یک Integer قرار دهید، بخش اعشاری آن از دست میرود.
هنگام اجرای برنامه اغلب به محلی برای ذخیرهٔ موقت داده نیاز دارید. برای نگهداری مقادیر در زمان اجرای کد از متغیرها استفاده میشود. درون یک Procedure، متغیر با دستور Dim و نام متغیر تعریف میشود:
Dim variablename [As type]
نام متغیرها باید این قواعد را رعایت کند:
- باید با یک حرف آغاز شوند.
- فقط میتوانند شامل حروف، اعداد یا کاراکتر زیرخط (_) باشند؛ فاصله مجاز نیست.
- نباید از ۴۰ کاراکتر بیشتر شوند.
- نباید یک واژهٔ رزروشده باشند؛ بخش «واژههای رزروشده» در پایان همین فصل را ببینید.
بخش اختیاری As type امکان تعیین نوع دادهٔ متغیری را که تعریف میکنید فراهم میکند. اگر نوع را حذف کنید، نوع پیشفرض Variant خواهد بود که در ادامه توضیح داده میشود.
Dim MyInteger as Integer
تعریف ضمنی
لازم نیست حتماً پیش از استفاده از یک متغیر آن را تعریف کنید. میتوانید مستقیماً دستور زیر را در کد قرار دهید:
TempVal=6
در این حالت متغیری با نام TempVal بهطور خودکار با نوع Variant ــ نوع پیشفرض ــ ساخته میشود و مقدار آن ۶ خواهد بود.
مشکل این روش آن است که اگر در دستور دیگری نام متغیر را اشتباه تایپ کنید، ممکن است خطاهای ظریفی در کد ایجاد شود. برای مثال، اگر بهجای tempval بنویسید temval، شما میدانید منظورتان چیست اما VBA نمیداند. VBA فرض میکند temval یک متغیر جدید است و آن را به همین صورت ایجاد میکند. متغیر قدیمی tempval همچنان وجود دارد ولی دیگر استفاده نمیشود. در نتیجه دو متغیر متفاوت دارید، در حالی که تصور میکنید فقط یک متغیر وجود دارد. چنین اشتباهی میتواند مشکلات بزرگی ایجاد کند که اصلاح آنها در کد زمانبر باشد.
تعریف صریح
برای جلوگیری از اشتباه در نام متغیرها میتوانید VBA را ملزم کنید هر بار که با متغیری تعریفنشده روبهرو میشود خطا ایجاد کند. برای این کار باید به بخش Declarations ماژول کد بروید. اگر در پنجرهٔ VB Editor یک ماژول را نگاه کنید، در بالا-چپ عنوان (General) و در بالا-راست عنوان (Declarations) را میبینید. روی (Declarations) کلیک کنید تا مستقیماً به بخش تعریفها بروید. نگران نباشید اگر به نظر میرسد در یک بخش مشخص تایپ نمیکنید؛ عبارت زیر را وارد کنید. بهمحض واردکردن یک Declaration، خطی بهطور خودکار در زیر آن ظاهر میشود تا نشان دهد این عبارت در بخش تعریفها قرار دارد.
Option Explicit
این دستور جلوی تعریفهای ضمنی را میگیرد. اکنون باید TempVal را تعریف کنید:
Dim TempVal
اگر هنگام اجرا به temval ارجاع دهید، پیام خطایی نمایش داده میشود که اعلام میکند متغیر تعریف نشده است.
انتخاب میان روش ضمنی یا صریح به ترجیح شخصی شما بستگی دارد. کدنویسی با روش ضمنی اغلب سریعتر است، زیرا لازم نیست پیش از استفاده همهٔ متغیرها را از ابتدا تعریف کنید؛ کافی است دستورهای مربوط به متغیر را بنویسید و VBA بقیهٔ کار را انجام میدهد. با این حال، همانطور که گفته شد، این روش میتواند منجر به خطا شود، مگر آنکه حافظهٔ خوبی برای نام متغیرهای مورد استفاده داشته باشید و تجربهٔ کافی برای دانستن دقیق کاری که انجام میدهید در اختیار داشته باشید. روش ضمنی همچنین فهم کد را برای شخص دیگری دشوارتر میکند. استفاده از Option Explicit روش پیشنهادی و بهترین رویه است و به جلوگیری از خطاهای زمان اجرا کمک میکند.
دامنه و طول عمر متغیرها
اگر متغیری را داخل یک Procedure تعریف کنید، فقط کد همان Procedure میتواند به آن متغیر دسترسی داشته باشد؛ دامنهٔ آن محلی است. اغلب به متغیرهایی نیاز خواهید داشت که چند Procedure یا حتی کل برنامه بتوانند از آنها استفاده کنند. به همین دلیل میتوان متغیر را در سطح محلی، سطح ماژول یا سطح Global تعریف کرد.
متغیرهای محلی
برای تعریف یک متغیر محلی درون یک Procedure از Dim، Static یا ــ فقط برای آرایهها ــ ReDim استفاده میشود. چند Procedure میتوانند متغیری با نام temp داشته باشند، اما چون هر متغیر نسبت به Procedure خود محلی است، همهٔ آنها مستقل از یکدیگر عمل میکنند و میتوانند مقادیر متفاوتی داشته باشند.
متغیرهای محلی که با Dim تعریف میشوند فقط تا زمانی وجود دارند که Procedure در حال اجراست. متغیرهای محلی تعریفشده با Static تا پایان عمر برنامه باقی میمانند. ممکن است بخواهید مقدار یک متغیر را در سراسر اجرای برنامه حفظ کنید؛ فصل ۲۱ نمونهای نشان میدهد که چگونه یک متغیر Static میتواند رفتار کد را تغییر دهد.
Dim TempVal
Static TempVal
همچنین میتوانید یک متغیر را بهصورت آرایهای با چند عنصر و حتی چند بُعد تعریف کنید. از نظر مفهومی، آرایه تقریباً شبیه Spreadsheet است. میتوانید آرایهای با ده عنصر ایجاد کنید که ده خانه برای ذخیرهٔ اطلاعات داشته باشد. اگر بُعد دیگری به آن بدهید، آرایهٔ ۱۰ در ۱۰ خواهید داشت که ۱۰۰ خانه برای نگهداری اطلاعات فراهم میکند. آرایه در ذخیرهسازی داده انعطاف بسیار زیادی میدهد؛ درست مانند قرار دادن دادهها در سلولهای جداگانهٔ Spreadsheet. برای نمونه، اگر بخواهید یک درایو را مانند Windows Explorer بهصورت بازگشتی برای همهٔ زیردایرکتوریها جستوجو کنید، به آرایهای نیاز دارید تا نام مسیرهایی را که پیدا میشوند ذخیره کنید و بعد بتوانید بهسادگی در برنامه به آنها ارجاع دهید.
Dim A()
ReDim A(10)
ReDim Preserve A(12)
برای استفاده از ReDim باید متغیر را در ابتدا بهعنوان آرایه تعریف کرده باشید. Dim A(3) آرایهای کوچک با چهار عنصر، از ۰ تا ۳، ایجاد میکند؛ بنابراین عملاً چهار متغیر A وجود دارد. سپس ReDim A(10) آن را به آرایهای ۱۱ عنصری تبدیل میکند، اما تمام دادههای قبلی از بین میروند. ReDim Preserve A(12) آرایهای ۱۳ عنصری میسازد و دادههای موجود را حفظ میکند. توجه کنید که بهطور پیشفرض همهٔ Subscriptها از ۰ شروع میشوند.
ReDim زمانی مفید است که برای ذخیرهٔ داده به آرایه نیاز دارید اما نمیدانید چند عنصر لازم خواهد بود. برای مثال، هنگام جستوجوی بازگشتی Directoryها نمیدانید چند Directory روی یک Disk وجود دارد؛ بنابراین میتوانید با یک آرایهٔ کوچک دهعنصری شروع کنید و وقتی پر شد با ReDim و Preserve اندازهٔ آن را افزایش دهید و دادههای موجود را نگه دارید.
متغیرهای سطح ماژول
متغیر سطح ماژول برای یک ماژول مشخص تعریف میشود. همهٔ Procedureهای همان ماژول به آن دسترسی دارند، اما سایر قسمتهای برنامه نمیتوانند از آن استفاده کنند. متغیرهای سطح ماژول تا پایان عمر برنامه وجود دارند و مقدار خود را حفظ میکنند.
Dim TempVal
این عبارت باید در بخش Declarations ماژول قرار گیرد، نه داخل یک Procedure خاص.
متغیرهای Global
متغیرهای Global در بخش Declarations یک ماژول و با دستور Global تعریف میشوند و هر کدی در برنامه میتواند به آنها دسترسی داشته باشد. این متغیرها در سراسر عمر برنامه وجود دارند و مقدارشان را حفظ میکنند.
Global TempVal
این عبارت نیز در بخش Declarations یکی از ماژولها قرار میگیرد. چون متغیر بهصورت Global تعریف شده است، از هر قسمت کد قابل دسترسی خواهد بود.
تعارض نامها و Shadowing
یک متغیر در هنگام اجرای کد نمیتواند دامنهٔ خود را تغییر دهد، اما میتوانید متغیری با نام یکسان در دامنه یا ماژولی دیگر داشته باشید. مثلاً یک متغیر Global با نام temp و یک متغیر محلی با همان نام درون یک Procedure داشته باشید. ارجاع به temp داخل Procedure به متغیر محلی دسترسی پیدا میکند و ارجاع به آن در خارج Procedure به متغیر Global مربوط میشود. در این حالت متغیر محلی، متغیرهای کممحلیتر را Shadow میکند؛ یعنی در اولویت دسترسی قرار میگیرد. تنها راه استفاده از متغیر Global بهجای متغیر محلی این است که نام متفاوتی برای آن انتخاب کنید. Shadowing میتواند گیجکننده باشد و خطاهای ظریفی تولید کند که Debug کردن آنها دشوار است. بهترین روش، استفاده از نامهای یکتا برای همهٔ متغیرهاست.
نام متغیرهای سطح ماژول و Global همچنین میتواند با نام Procedureها تعارض پیدا کند. یک Procedure یا Subroutine، مگر آنکه بهصورت Private تعریف شده باشد، دامنهٔ Global دارد؛ این موضوع در فصل ۳ توضیح داده میشود. یک متغیر Global نمیتواند با هیچ Procedure عمومی در هیچ ماژول کدی نام یکسان داشته باشد.
متغیرهای Static
متغیرها بر اساس دامنهٔ خود طول عمر متفاوتی نیز دارند. متغیرهای Module و Global برای تمام عمر برنامه حفظ میشوند؛ یعنی تا وقتی برنامه در حال اجراست و کاربر آن را نبسته، مقدار خود را نگه میدارند. متغیرهای محلی تعریفشده با Dim فقط هنگام اجرای Procedure وجود دارند. هنگامی که Procedure متوقف میشود، مقدارها حفظ نمیشوند و حافظه آزاد میشود. اجرای بعدی، متغیرها را دوباره برای طول عمر همان Procedure مقداردهی اولیه میکند. متغیر محلی باید فقط برای مقادیری استفاده شود که در همان Procedure مورد نیازند. اگر انتظار دارید ماژولهای دیگر نیز به آن دسترسی داشته باشند، متغیر باید Global باشد.
با این حال میتوانید با واژهٔ کلیدی Static یک متغیر محلی را تعریف کرده و مقدار آن را حفظ کنید:
Static Temp
همچنین میتوانید با قرار دادن واژهٔ Static در ابتدای عنوان Procedure، تمام متغیرهای محلی آن را Static کنید:
Static Sub Test_MyProc()
انواع داده
میتوان برای یک متغیر نوع دادهای تعیین کرد که مشخص میکند چه نوع اطلاعاتی را میتواند ذخیره کند. انتخاب نوع داده روی کارایی کد نیز اثر دارد. اگر نوعی تعیین نشود، نوع پیشفرض Variant خواهد بود.
Variant
نوع Variant میتواند انواع مختلف داده، از متن و عدد گرفته تا تاریخ و اطلاعات دیگر را ذخیره کند؛ حتی یک آرایهٔ کامل نیز میتواند در آن قرار گیرد. متغیر Variant میتواند در زمان اجرا آزادانه نوع خود را تغییر دهد، در حالی که متغیری که مثلاً String تعریف شده چنین امکانی ندارد. با تابع VarType میتوان فهمید یک Variant در حال حاضر چه نوع دادهای را در خود نگه میدارد:
Sub TestVariables()
stemp = "richard"
MsgBox VarType(stemp)
stemp = 4
MsgBox VarType(stemp)
End Sub
کادر پیام ابتدا مقدار ۸ را نمایش میدهد که نشاندهندهٔ String است و سپس مقدار ۲ را نشان میدهد که به معنی Integer است.
جدول ۲-۱ مقادیر برگشتی برای انواع دادهٔ مشخص را نشان میدهد.
جدول ۲-۱ — مقادیر برگشتی VarType| مقدار برگشتی | نوع |
| 0 | Variant |
| 1 | Null |
| 2 | Integer |
| 3 | Long |
| 4 | Single |
| 5 | Double |
| 6 | Currency |
| 7 | Date/Time |
| 8 | String |
| 11 | Boolean |
| 17 | Byte |
VBA همیشه تلاش میکند دادهٔ درون Variant را با کارآمدترین روش ذخیره کند. همانطور که در مثال قبل دیده شد، نوع Variant بهصورت خودکار با دادهای که در آن ذخیره میشود تطبیق پیدا میکند.
اگر روی Variantای که مقدار عددی ندارد یک عمل ریاضی انجام دهید، خطای Type Mismatch دریافت میکنید. یعنی تلاش کردهاید نوعی از داده را در متغیری قرار دهید که برای آن نوع تنظیم نشده است؛ شبیه تلاش برای فروکردن یک قطعهٔ مربع در سوراخی گرد. در چنین حالتی ممکن است روی Variantای که یک رشتهٔ متنی نگه میدارد عمل ریاضی انجام داده باشید.
مقادیر تاریخ و زمان در Variant
متغیرهای Variant میتوانند مقدارهای Date/Time را نیز نگه دارند. این مقدار یک عدد Floating Point است: بخش صحیح تعداد روزهای گذشته از 31-Dec-1899 را نشان میدهد و بخش اعشاری، ساعت و دقیقه و ثانیه را بهصورت نسبتی از ۲۴ ساعت بیان میکند. برای مثال مقدار 37786.75 برابر با ساعت ۱۸:۰۰ در 14-June-2003 است. فاصلهٔ 31-Dec-1899 تا 14-June-2003 برابر ۳۷٬۷۸۶ روز است و ۰٫۷۵ از ۲۴ ساعت نیز ۱۸ ساعت میشود.
افزودن یا کمکردن عدد صحیح، روزها را اضافه یا کم میکند. افزودن مقدار اعشاری، زمان روز را افزایش میدهد؛ مثلاً افزودن 1/24 (0.0416) یک ساعت به زمان اضافه میکند. چندین تابع برای کار با تاریخ و زمان وجود دارد که در فصل ۵ توضیح داده میشوند.
توجه کنید که تفسیر روز و ماه به تنظیمات Regional Options در Control Panel ویندوز وابسته است. اگر در Regional Options قالب تاریخ را mm/dd/yy تعیین کنید، تفسیر پیشفرض روز و ماه بر همان اساس خواهد بود.
همانطور که با IsNumeric میتوانید عددیبودن مقدار را بررسی کنید، با تابع IsDate نیز میتوان وجود یک مقدار تاریخ را تشخیص داد:
temp = "01-Feb-2009"
MsgBox IsDate(temp)
نتیجه True، یعنی مقدار غیرصفر، خواهد بود.
مقدار Empty
Variantای که هنوز مقداری به آن اختصاص داده نشده باشد دارای مقدار Empty است. با تابع IsEmpty میتوان این وضعیت را بررسی کرد:
MsgBox IsEmpty(MyTest)
در این مثال نتیجه True است، زیرا هنوز مقداری به MyTest اختصاص داده نشده است.
مقادیر Null
Variant میتواند مقدار ویژهٔ Null را نگه دارد. Null برای نشاندادن دادهٔ ناشناخته یا گمشده به کار میرود. متغیرها خودبهخود Null نمیشوند، مگر آنکه کدی بنویسید که این مقدار را به آنها اختصاص دهد. اگر در برنامهٔ خود از Null استفاده نکنید، لازم نیست نگران آن باشید.
مطمئنترین راه برای بررسی مقدار Null در کد استفاده از IsNull است. روشهای دیگر، مانند عبارت Is Null، ممکن است نتیجهٔ درست ندهند.
Sub TestNull()
temp=Null
Msgbox IsNull(temp)
End Sub
نتیجه در این مثال True، یعنی مقدار غیرصفر، خواهد بود.
سایر انواع داده
چرا باید بهجای Variant از انواع دادهٔ دیگر استفاده کرد؟ چون Variant الزاماً بهترین نوع داده را برای هدف موردنظر انتخاب نمیکند. اگر میخواهید کدی فشرده و سریع داشته باشید، به انواع دادهٔ دیگر نیاز دارید. برای مثال، اگر محاسبات ریاضی فراوانی روی اعداد صحیح نسبتاً کوچک انجام میدهید، استفاده از نوع Integer بهجای Variant میتواند سرعت بسیار بیشتری ایجاد کند. میتوانید Variant را بهعنوان نوع پیشفرض به کار ببرید، اما Variant الزاماً فرض نمیکند داده یک Integer است؛ ممکن است آن را Floating Point در نظر بگیرد و در این صورت محاسبات بسیار بیشتر طول میکشند، هرچند نتیجهٔ نهایی یکسان است.
مصرف حافظه نیز باید در نظر گرفته شود. هر عدد Double هشت Byte حافظه مصرف میکند که در نگاه اول زیاد به نظر نمیرسد، اما در یک آرایهٔ بزرگ میتواند مقدار قابلتوجهی RAM اشغال کند و پردازش را کند سازد. این حافظه بخشی از RAM رایانه است که Windows میتوانست برای حافظهٔ مجازی و نمایش گرافیکی خود استفاده کند.
انواع دادهٔ VBA
در VBA چندین نوع داده قابل استفاده است. جزئیات آنها در جدول ۲-۲ آمده است.
انواع عددی
اگر فقط با اعداد صحیح کار میکنید، بسته به اندازهٔ عدد متغیرها را بهصورت Integer یا Long تعریف کنید. عملیات ریاضی با این انواع بسیار سریعتر است و حافظهٔ کمتری نیاز دارد.
اگر با بخشهای کسری عدد سروکار دارید، از Single، Double یا Currency استفاده کنید. Currency که نقطهٔ اعشار ثابت دارد تا ۴ رقم در سمت راست اعشار و ۱۵ رقم در سمت چپ آن را پشتیبانی میکند. انواع Floating Point یعنی Single و Double دامنهٔ بزرگتری دارند، اما ممکن است خطاهای بسیار کوچک گردکردن ایجاد کنند.
Dim temp1 as Integer
Dim temp2 as Long
Dim temp3 as Currency
Dim temp4 as Single
Dim temp5 as Double
انواع رشتهای
اگر متغیر همیشه حاوی متن خواهد بود، میتوانید آن را از نوع String تعریف کنید:
Dim temp as String
سپس میتوانید با توابع پردازش String آن را دستکاری کنید؛ بخشی از آن را بردارید، یک کاراکتر خاص را جستوجو کنید یا همهٔ حروف را به Uppercase تبدیل کنید. توضیح کاملتر در بخش «Functions» فصل ۵ آمده است.
بهطور پیشفرض طول String متغیر است و متناسب با دادهٔ داخل آن بزرگ یا کوچک میشود. اگر نخواهید چنین رفتاری داشته باشد، میتوانید با String * size یک رشتهٔ طولثابت تعریف کنید:
Dim temp as String * 50
این دستور طول رشته را دقیقاً ۵۰ کاراکتر میکند. اگر متن کوتاهتر از ۵۰ کاراکتر باشد با Space پر میشود و اگر بیشتر از ۵۰ کاراکتر باشد، کاراکترهای اضافی بریده و از دست میروند. بنابراین هرچند کنترل مشخصی روی حافظهٔ مصرفی هر عنصر دارید، اگر کاربر رشتهای طولانیتر از چیزی که پیشبینی کردهاید وارد کند خطر از دسترفتن داده وجود دارد.
جدول ۲-۲ — انواع داده در VBA| نام | توضیح | کاراکتر تعریف نوع | دامنه |
| Integer | عدد صحیح دو بایتی | % | –32,768 تا 32,767 |
| Long | عدد صحیح چهار بایتی | & | –2,147,483,648 تا 2,147,438,647 |
| Single | عدد Floating Point چهار بایتی | ! | –3.402823E38 تا 1.401298E-45 برای مقادیر منفی؛ 1.401298E-45 تا 3.402823E38 برای مقادیر مثبت |
| Double | عدد Floating Point هشت بایتی | # | –1.79769313486232E308 تا –4.94065645841247E-324 برای منفی؛ 4.94065645841247E-324 تا 1.79769313486232E308 برای مثبت |
| Currency | عدد هشت بایتی با نقطهٔ اعشار ثابت | @@ | –922337203685477.5808 تا 922337203685477.5807 |
| Fixed Length String | رشتهٔ کاراکتری با طول ثابت | $ | ۰ تا حدود ۶۵٬۴۰۰ کاراکتر |
| Variable Length String | رشتهٔ کاراکتری با طول متغیر | $ | ۰ تا حدود ۲ میلیارد کاراکتر |
| Variant | تاریخ/زمان، عدد Floating Point یا رشته | ندارد | تاریخ: ۱ ژانویهٔ ۰۰۰۰ تا ۳۱ دسامبر ۹۹۹۹؛ اعداد: همان دامنهٔ Double؛ رشته: همان دامنهٔ String |
آرایهها
تا اینجا دربارهٔ متغیرهای منفرد صحبت شد. میتوانید یک متغیر ایجاد کنید و مقداری مانند عدد یا رشته به آن بدهید. نمونهٔ ساده، نام یک کارمند است؛ متغیری به نام employee میسازید و نام کارمند را بهصورت String در آن قرار میدهید. اما اگر کارمندان بیشتری وجود داشته باشند چه؟ فرض کنید برنامه باید به ۲۶ کارمند ارجاع دهد. استفاده از متغیرهای ساده مدیریت آنها را بسیار دشوار میکند و مجبور میشوید چیزی شبیه کد زیر بنویسید:
Dim employee1 as String, employee2 as String, employee3 as String,.......
این روش بسیار دستوپاگیر و ناکارآمد است. اگر کارمند جدیدی اضافه شود چه میکنید؟ برنامهٔ شما دیگر درست کار نخواهد کرد.
خوشبختانه میتوان یک متغیر را بهصورت آرایه تعریف کرد. کافی است بنویسید:
Dim employee(25) as String
همانطور که پیشتر گفته شد، آرایه عملاً مانند مجموعهای از خانهها یا سلولهای Spreadsheet است که با Index آرایه میتوان داده را در آنها خواند و نوشت. برای تعیین عنصر موردنظر، شمارهٔ Subscript داخل پرانتز نوشته میشود. در صورت نیاز برنامه میتوانید در زمان اجرا با ReDim آرایه را بزرگتر کنید.
مثال بالا آرایهای ۲۶ عنصری از شمارهٔ ۰ تا ۲۵ ایجاد میکند که ۲۶ رشته برای ذخیرهٔ نامها در اختیار دارد. هر عنصر با شمارهٔ Index خود در کد قابل دسترسی است. برای فهرستکردن همهٔ نامهای آرایه بهسادگی میتوان از حلقهٔ For..Next استفاده کرد:
Dim employee(25) as String
For n = 0 To 25
employee(n) = Chr(n+65)
Next n
For n = 0 To 25
MsgBox employee(n)
Next n
در این ماکرو ابتدا آرایهٔ employee بهصورت String و با ۲۶ عنصر تعریف میشود. حلقهٔ اول For..Next داده را در آرایه قرار میدهد. کد ASCII ــ American Standard Code for Information Interchange، ضمیمه را ببینید ــ برای حرف A برابر ۶۵ است و این مقدار به n که از صفر شروع میشود اضافه میگردد. تابع Chr آن مقدار را به کاراکتر تبدیل میکند و کاراکتر در عنصر مناسب آرایه قرار میگیرد. در اولین دور حلقه، چون n برابر صفر است، حرف A در اولین عنصر آرایه گذاشته میشود؛ مقدار ۶۵، یعنی کد A، به صفر اضافه شده است.
حلقهٔ دوم For..Next هر عنصر آرایهٔ employee را بهترتیب نمایش میدهد. با اجرای مثال، حروف A تا Z نمایش داده میشوند.
آرایهها همان قواعد متغیرهای عادی را دنبال میکنند. میتوانند Local، Module یا Global باشند و هر نوع دادهای، از جمله Variant، داشته باشند. اندازهٔ آرایه از نظر تعداد عنصر به محدودهٔ Integer یعنی –32,768 تا 32,767 محدود است. مرز پایین پیشفرض همیشه صفر است، اما با قرار دادن دستور Option Base در بخش Declarations ماژول میتوان آن را تغییر داد:
Option Base 1
در این حالت همهٔ آرایههای آن ماژول از ۱ آغاز میشوند.
همچنین میتوانید با واژهٔ کلیدی To حد پایین آرایه را مستقیماً مشخص کنید:
Dim temp (1 To 15) as String
آرایههای چندبعدی
تا اینجا فقط آرایههای یکبعدی بررسی شدند، اما یک آرایه میتواند چند بُعد داشته باشد. دوباره آن را مانند Spreadsheet تصور کنید: سطر و ستون یک مرجع ایجاد میکنند و میتوانید چند Sheet متفاوت نیز داشته باشید، بهطوری که مرجع هر Cell از نام Sheet بههمراه ستون و سطر تشکیل شود.
Dim temp(10,4) as String
اگر این ساختار یک Spreadsheet بود، ۱۱ ستون و ۵ سطر داشت و در مجموع ۵۵ خانه یا سلول برای قراردادن و ارجاع به دادهها فراهم میکرد.
آرایهٔ سهبعدی به شکل زیر است:
Dim temp(10,4,3) as String
اگر دوباره آن را مانند Spreadsheet تصور کنیم، ۱۱ ستون و ۵ سطر دارد که روی چهار Worksheet گسترده شدهاند؛ در نتیجه در مجموع ۲۲۰ خانه در اختیار خواهید داشت. اگر به خاطر بیاورید که هر یک از این عناصر میتواند رشتهای تا حدود ۶۵٬۴۰۰ کاراکتر را نگه دارد، مشخص میشود حتی یک آرایهٔ ساده چه مقدار حافظه میتواند مصرف کند و چه حجم دادهای در آن قابل ذخیره است.
تعریف ابعاد آرایه بلافاصله حافظه را به آن اختصاص میدهد و این هنگام طراحی برنامه موضوع مهمی است. اشغال بخشهای بزرگ حافظه میتواند کارایی برنامه و Windows را کاهش دهد. Windows خود یک برنامهٔ گرافیکی است و برای نگهداری اطلاعات مقدار زیادی RAM مصرف میکند. ممکن است استفاده از آرایهای بزرگ باعث کندشدن Windows شود و برنامههای دیگر نیز آهستهتر اجرا شوند و پردازش اطلاعات زمان بیشتری ببرد. این وضعیت شاید روی رایانهٔ شخصی قابل قبول باشد، اما در یک برنامهٔ حرفهای باید به آن توجه شود.
ابعاد بیشتر نیز امکانپذیرند، اما دنبالکردن و مدیریت آنها دشوار میشود. پنج بُعد معمولاً بیشترین تعداد امن و قابلمدیریت در نظر گرفته میشود. اگر آرایه را مجموعهای از Spreadsheetها تصور کنید، کافی است پیچیدگی یک Spreadsheet پنجبعدی را در ذهن بیاورید.
از ReDim همچنان میتوان برای تغییر اندازهٔ آرایه استفاده کرد، اما نمیتوان با آن تعداد ابعاد یا نوع آرایه را، مثلاً از String به Integer، تغییر داد.
آرایههای پویا
گاهی از قبل نمیدانید آرایه باید چه اندازهای داشته باشد. نمونهٔ مناسب زمانی است که مسیرهای یک دستگاه Disk را بهصورت بازگشتی در عناصر آرایه ذخیره میکنید. نمیدانید چند Subdirectory وجود دارد یا یک شاخه تا چه عمقی ادامه مییابد. ممکن است آرایه را ۱۰۰۰ عنصری تعریف کنید و حافظهٔ ارزشمند را اشغال کنید، اما بعد مشخص شود فقط ۵۰۰ عنصر لازم بوده است. از طرف دیگر شاید ۲۰۰۰ مسیر Subdirectory وجود داشته باشد و فضای آرایه کافی نباشد.
آرایهٔ پویا درست مانند آرایهٔ معمولی ساخته میشود: با Dim در سطح Global، Module یا Local، یا با Static در سطح Local. فهرست ابعاد آن خالی گذاشته میشود:
Dim temp()
سپس درون Procedure با ReDim تعداد عناصر آرایه را تغییر میدهید. ReDim فقط داخل Procedure قابل استفاده است و تعداد ابعاد را نمیتواند تغییر دهد:
ReDim temp(100)
میتوانید کدی بنویسید که تعداد مقادیر جمعآوریشده را بررسی کند و وقتی آرایه به مرز بالایی نزدیک میشود اندازهٔ آن را افزایش دهد. دو تابع LBound و UBound در کار با آرایهها مفیدند و با مشخصکردن آرایه میتوانند حد پایین و بالای ابعاد آن را برگردانند:
Dim temp(10)
MsgBox LBound(MyTemp)
MsgBox UBound(MyTemp)
LBound مقدار ۰ و UBound مقدار ۱۰ را برمیگرداند.
دستور ReDim بهطور خودکار همهٔ مقادیر آرایه را پاک میکند، مگر آنکه از واژهٔ کلیدی Preserve استفاده کنید:
ReDim Preserve temp(100)
در این حالت دادههایی که از قبل در temp قرار داشتهاند حفظ خواهند شد.
انواع تعریفشده توسط کاربر
با استفاده از انواع متغیر موجود و واژهٔ کلیدی Type میتوانید نوع متغیر مخصوص خودتان را نیز تعریف کنید. این تعریف باید در بخش Declarations یک ماژول قرار گیرد:
Type Employee
Name as String
Salary as Currency
Years as Integer
End Type
این کد نوع جدیدی با نام Employee ایجاد میکند که اطلاعات مربوط به Name، Salary و Years of Service را نگه میدارد.
سپس میتوانید از این نوع دقیقاً مانند انواع داخلی متغیر استفاده کنید. حتی بهطور خودکار در فهرستهای کشویی ویرایشگر VBA نیز ظاهر میشود. این نوع را میتوان مانند یک متغیر عادی به کار برد:
Dim temp As employee
temp.Name = "Richard Shepherd"
temp.Salary = 10000
temp.Years = 5
MsgBox temp.Name
MsgBox temp.Salary
MsgBox temp.Years
هنگام تایپ نام متغیر در کد، List Boxای نمایش داده میشود که Propertyها یا Fieldهای مربوط به این نوع داده را نشان میدهد.
همچنین میتوانید آرایهای از این نوع ایجاد کنید و عملاً آن را مانند یک شیء مستقل به کار ببرید. توجه کنید پس از مشخصکردن نام آرایه و Index عنصری که داده را دریافت میکند، یک نقطه قرار داده میشود تا Name مانند یک Property ظاهر شود:
Dim temp(10) as employee
temp(0).Name = "Richard Shepherd"
ثابتها
ثابتها در عمل متغیرهایی هستند که تغییر نمیکنند. آنها مقادیری را نگه میدارند که در کد بارها استفاده میشوند و یک نام کوتاه برای مقدار ثابت موردنظر فراهم میکنند.
قواعد ساخت Constant شبیه قواعد متغیر است، اما برخلاف متغیر نمیتوانید از داخل کد مقدار جدیدی به آن اختصاص دهید.
Const Path_Name = "C:\temp\"
این کد Constantای با نام Path_Name ایجاد میکند که در اینجا همیشه مقدار C:\temp\ را دارد. بنابراین هر بار که در برنامه به این مسیر نیاز دارید میتوانید از همین نام استفاده کنید.
مدل شیء Access نیز Constantهای ازپیشتعریفشدهای دارد که با Object Browser قابل مشاهدهاند؛ Object Browser در فصل ۱۲ بررسی میشود. در متن منبع آمده است که Constantهای مدل شیء با حروف xl آغاز میشوند، مانند xlSaveChanges یا xlDoNotSaveChanges. Object Browser همچنین مقدار واقعی Constant را در پایین پنجرهٔ مرورگر نمایش میدهد.
واژههای رزروشده
احتمالاً متوجه شدهاید که VBA تعدادی Keyword دارد که اجزای زبان را تشکیل میدهند؛ مانند For، Next، Do و Loop. این واژهها را نمیتوان برای نامگذاری متغیر، Subroutine یا Function استفاده کرد، زیرا Reserved Word هستند. یعنی بخشی از خود زبان VBA محسوب میشوند و اگر اجازه داشتید آنها را برای منظورهای دلخواه در کد به کار ببرید، ابهام بسیار زیادی ایجاد میشد. خوشبختانه VBA هنگام تایپ بلافاصله کد را بررسی میکند و معمولاً خطای Expected Identifier نمایش میدهد؛ این خطا نشان میدهد از یک واژهٔ رزروشده استفاده کردهاید و VBA تصور میکند در حال واردکردن یک دستور برنامه هستید.
عبارت زیر را امتحان کنید:
Dim Loop as String
Loop بخشی از VBA و مورد استفاده در دستورهای Do Loop است؛ بنابراین واردکردن این دستور ممکن نیست. بلافاصله خطا ظاهر میشود و خط کد به رنگ قرمز درمیآید تا وجود مشکل را نشان دهد. میتوانید هشدار را نادیده بگیرید، اما بهمحض اجرای کد دوباره خطا دریافت خواهید کرد. حال عبارت زیر را امتحان کنید:
Sub ReDim()
باز هم پیام خطا نشان داده میشود و کد قرمز میشود، زیرا ReDim یک Keyword در VBA است.
جالب اینکه میتوانید بعضی واژههای مدل شیء Access را بهعنوان نام به کار ببرید. مثلاً یک Subroutine را Application یا Worksheets بنامید و کد کار خواهد کرد. با این حال چنین کاری توصیه نمیشود. نویسنده مواردی دیده است که به دلیل استفاده از واژههای مدل شیء، هنگام بستهشدن برنامه کد بهدرستی خاتمه پیدا نکرده است. این نامگذاری در VBA سردرگمی ایجاد میکند و بهتر است از آن پرهیز شود.
بهطور کلی، هیچ Keyword یا Function مربوط به VBA را نباید بهعنوان نام متغیر، Subroutine یا Function استفاده کرد.