تعتبر لغة برمجة التطبيقات للمكاتب (Visual Basic for Applications – VBA) واحدة من أكثر لغات البرمجة النصية رسوخاً وفاعلية في بيئات العمل المؤسسية والمالية؛ إذ وفرت منذ إطلاقها بواسطة شركة مايكروسوفت (Microsoft) في عام 1993 ضمن حزمة Excel 5.0 بديلاً متطوراً للغات الماكرو القديمة مثل لغة XLM. وتعود الجذور التاريخية لهذه البيئة البرمجية إلى مشروع “Ruby” الذي ابتكره آلان كوبر (Alan Cooper) في أواخر ثمانينيات القرن العشرين، قبل أن تستحوذ عليه مايكروسوفت لتدشين لغة فيجوال بيسك (Visual Basic 1.0) في عام 1991، والتي انبثقت منها بنية VBA لتمكين المطورين والمحللين من أتمتة المهام المعقدة وتوسيع القدرات الوظيفية القياسية لحزم البرمجيات المكتبية.
يرتكز البناء البرمجي عالي الكفاءة في VBA على الاستخدام السليم للوحدات النمطية الفرعية، وتحديداً التمييز الدقيق بين الإجراءات التي تنفذ مهاماً تنفيذية بحتة دون إعادة ناتج، وبين الدوال الحسابية والمنطقية (Functions) المصممة لاحتساب البيانات وتمرير المخرجات عبر مكدس الاستدعاء البرمجي. وتعد مسألة “كيفية إرجاع قيمة من دالة VBA” الركيزة الأساسية التي تدور حولها المفاهيم البرمجية المتقدمة في بناء الدوال المعرفة بواسطة المستخدم (User-Defined Functions – UDFs) وتطوير الأنظمة التركيبية المعقدة، حيث يؤدي استيعاب هذه الآلية إلى تفادي الأخطاء الشائعة المتعلقة بالأنواع البيانية واستهلاك الذاكرة.
يهدف هذا المقال الأكاديمي الشامل إلى تقديم تحليل بنيوي وهندسي دقيق لآليات إرجاع القيم في بيئة VBA، بدءاً من القواعد النحوية الصريحة لإسناد القيمة إلى اسم الدالة، مروراً بالتعامل مع أنواع البيانات البسيطة والمعقدة كالمصفوفات والكائنات، ووصولاً إلى أحدث المعايير البرمجية في التحكم في مسارات التنفيذ، ومعالجة الأخطاء الاستثنائية، وتحسين الأداء الحسابي في المصنفات الضخمة.
1. المدخل المفاهيمي لدوال VBA وطبيعة القيمة المرجعة
1.1 الفرق البنيوي بين الإجراءات الفرعية (Sub) والدوال (Function)
تتمايز لغة VBA بنيوياً بين صنفين أساسيين من الكتل البرمجية الإجرائية: الإجراءات الفرعية المعرفة بالكلمة المفتاحية Sub، والدوال المعرفة بالكلمة المفتاحية Function. يكمن التباين الوظيفي الجوهري بينهما في أن الإجراء الفرعي مصمم حصرياً لتنفيذ سلسلة متتابعة من العمليات والأوامر التنفيذية التي تغير من حالة النظام أو الواجهة التفاعلية (مثل تنسيق الخلايا، فتح النوافذ، حفظ الملفات، أو نقل مؤشر الفأرة)، دون أن يمتلك الإجراء في ذاته القدرة على تمرير ناتج حسابي مباشر إلى التعبير البرمجي الذي قام باستدعائه.
في المقابل، تمثل الدالة كياناً حسابياً قائماً على نظرية التجريد الرياضي، حيث تستقبل معطيات مدخلة وتجري عليها معالجات خوارزمية محددة لتوليد قيمة مفردة أو بنية بيانات مدمجة، ثم تقوم بإعادة توجيه هذه النتيجة إلى بيئة الاستدعاء. من الناحية المعمارية في سياق الذاكرة، عند استدعاء إجراء فرعي، يتم تخصيص إطار في مكدس الذاكرة (Call Stack) لتنفيذ الأوامر، وبمجرد انتهاء الإجراء يتم تدمير الإطار دون ترك أي أثر لقيمة حية باستثناء الآثار الجانبية الناتجة عن تعديل المتغيرات العامة أو تمرير المعاملات بالمرجع. أما في حالة الدالة، فإن المترجم يحتفظ بموقع مخصص في الذاكرة لتخزين القيمة المرجعة وتمريرها مباشرة إلى المتغير المستقبل أو التعبير الحسابي المستدعي.
1.2 أهمية إرجاع القيم في هندسة البرمجيات وأتمتة المهام
يمثل إرجاع القيم حجر الزاوية في تطبيق مبادئ البرمجة التركيبية (Modular Programming) ونمط البرمجة الإجرائية النظيفة؛ إذ يتيح تقسيم المشكلات الحسابية الكبرى إلى وحدات صغيرة ومستقلة تؤدي كل منها وظيفة محددة بدقة بالغة. يؤدي هذا التقسيم إلى القضاء التام على تكرار الأكواد الحسابية المعقدة، فبدلاً من إعادة كتابة معادلة احتساب القيمة الزمنية للنقود أو تحليل النصوص في عشرات المواضع داخل المشروع، يتم حصر هذه الخوارزمية داخل دالة مفردة تعيد القيمة المحسوبة فور استدعائها، مما يسهل عمليات الصيانة والتحديث المستقبلي دون المخاطرة بإدخال أخطاء جانبية.
علاوة على ذلك، يمنح أسلوب إرجاع القيم استقلالية كاملة للاختبارات المعيارية والتحقق البرمجي (Unit Testing). فعندما تكون الدالة معزولة وتعتمد فقط على مدخلاتها لإنتاج مخرجاتها المرجعة دون الاعتماد على حالة التطبيق الخارجية، يمكن للمطورين كتابة إجراءات اختبارية تلقائية تتحقق من صحة النتائج عند تزويد الدالة بمدخلات حدية متنوعة، وهو ما يعزز موثوقية الأنظمة المصرفية والمحاسبية المؤتمتة عبر بيئات الجداول الإلكترونية.
1.3 دورة حياة الدالة من الاستدعاء حتى تسليم النتيجة
تبدأ دورة حياة الدالة فور قيام البرنامج التنفيذي بمقابلة استدعاء صريح لها في سطر برمجي أو صيغة رياضية داخل الخلية. في هذه اللحظة، يتم تعليق تنفيذ الروتين المستدعي وتخصيص مساحة تخزينية مؤقتة ضمن مكدس النظام (Call Stack Frame). يتم في هذا الإطار حجز المساحات الخاصة بالمعاملات المدخلة (Parameters) والمتغيرات المحلية (Local Variables)، إضافة إلى تهيئة المتغير الداخلي غير المرئي الحامل لاسم الدالة نفسه، والذي يمثل وعاء تسليم النتيجة النهائية وفقاً لمواصفات لغة VBA الصادرة عن مايكروسوفت.
تدخل الدالة إثر ذلك مرحلة المعالجة؛ حيث يتم تنفيذ التعليمات البرمجية سطراً تلو الآخر داخل هذا النطاق المغلق. وخلال هذه المرحلة، قد تتغير حالة المتغيرات المحلية وتتم عمليات الحساب المتشعبة. وعند اكتمال المعالجة وتعيين القيمة لاسم الدالة، يصل مؤشر التنفيذ إلى العبارة الختامية، حيث تبدأ عملية تفريغ المكدس وتحرير كافة الموارد والمتغيرات التي تم إنشاؤها محلياً، مع الحفاظ الصارم على القيمة المودعة في المتغير الاسمي للدالة لنقلها عبر السجل المعماري إلى نقطة الاستدعاء الأصلية لاستئناف تنفيذ البرنامج العام.
2. الآلية الأساسية لإرجاع قيمة في VBA: التعيين لاسم الدالة
2.1 قاعدة التعيين الاسمي الصريح (Name Assignment Rule)
تتفرد لغة Visual Basic for Applications بنمط نحوي خاص لإرجاع القيم يختلف جذرياً عن غالبية اللغات المعاصرة مثل C++ أو Python أو Java؛ فبدلاً من استخدام عبارة أمرية مثل return x; لنقل القيمة وإنهاء التنفيذ في خطوة موحدة، تنص مواصفات لغة VBA القياسية (MS-VBAL Specification §5.3.1) على أن الإعلان عن أي دالة يؤدي تلقائياً إلى إنشاء متغير محلي ضمني يحمل الاسم المطابق تماماً لاسم الدالة ونوع بياناتها المصرّح به. وتتم عملية إرجاع القيمة ببساطة عن طريق معاملة اسم الدالة كمتغير محلي اعتيادي يتم إسناد القيمة أو التعبير الحسابي إليه عبر معامل الإسناد الرياضي (=).
يترتب على هذا البناء النحوي حقيقة تقنية بالغة الأهمية: إن مجرد إسناد قيمة لاسم الدالة لا يعني إطلاقاً إيقاف أو إنهاء تنفيذ الأسطر التالية داخل الدالة، بل يستمر الكود في العمل حتى الوصول إلى النهاية الطبيعية. وإذا أهمل المبرمج إسناد أي قيمة لاسم الدالة طوال فترة التنفيذ، فإن الدالة لن تنهار برمجياً، بل ستعود بالنتيجة التلقائية الافتراضية المرتبطة بنوع البيانات المصرّح به، وهو ما قد يقود إلى أخطاء منطقية صامتة يصعب تتبعها في التطبيقات المالية.

2.2 تحديد القيمة الافتراضية عند عدم الإسناد
عندما تفشل الدالة في الوصول إلى سطر يقوم بتعيين قيمة صريحة لاسمها قبل بلوغ نقطة الخروج، تتدخل بيئة تشغيل VBA لإسناد القيمة الافتراضية المحددة للنوع البياني المذكور في ترويسة الدالة:
- الأنواع الرقمية (Integer, Long, Single, Double, Currency): تعيد الدالة القيمة الصفرية الحسابية (0) بصورة تلقائية، بغض النظر عن المدخلات التي استقبلتها الدالة.
- النوع النصي (String): يتم إرجاع سلسلة نصية فارغة تماماً بطول صفري وتعرف برمجياً بالرمز
""أو الثابتvbNullString. - النوع المنطقي (Boolean): يتم إرجاع القيمة المنطقية الزائفة
Falseبصورة افتراضية. - أنواع الكائنات (Object Types): عند الإعلان عن إرجاع كائن مثل
RangeأوWorksheet، فإن عدم تعيين كائن صريح يعيد المؤشر الفارغ المرجعيNothing. - النوع العام (Variant): إذا لم يتم تحديد نوع للدالة أو حدد كـ Variant، تعيد الدالة الحالة الخاصة
Emptyوالتي تعبر عن متغير غير مهيأ.
2.3 التسلسل الزمني لعملية التعيين قبل الخروج من الدالة
تخضع عملية إسناد النتيجة لاستقرار زمني مرن طوال فترة حياة الدالة؛ إذ يمتلك المطور حرية كاملة في إعادة كتابة وتعديل القيمة المسندة لاسم الدالة مرات متعددة أثناء سير المعالجة الداخلية. فالقيمة المستقرة في متغير اسم الدالة تظل قابلة للتحوير كأي متغير مؤقت آخر، حتى تحل اللحظة التي يغادر فيها مؤشر التعليمات نطاق الدالة رسمياً عبر العبارة الختامية End Function (أو عبر أمر الخروج المبكر).
تعتبر العبارة End Function نقطة التسليم الرسمية والنهائية للبيانات؛ حيث يتم في هذه اللحظة إغلاق النطاق المحلي وتثبيت القيمة الموجودة داخل الحاوية الاسمية للدالة في الذاكرة لنقلها إلى المتغير الطالب للنتيجة. ولذلك، فإن أفضل الممارسات الهندسية تدعو إلى إرجاء التعيين النهائي لاسم الدالة حتى المراحل الأخيرة من الخوارزمية، أو التأكد من أن التعيينات المرحلية لا يتم استبدالها بقيم غير مقصودة نتيجة استمرار تنفيذ حلقات تكرارية أو شروط فرعية متأخرة داخل الجسم البرمجي.
3. التحليل النحوي وتصريح أنواع البيانات المرجعة
3.1 صياغة الترويسة القياسية وتحديد النوع بواسطة العبارة As
تتطلب الكتابة المهنية المحترفة للأكواد البرمجية في VBA الالتزام بالترويسة القياسية للإعلان عن الدوال، والتي تفرض تحديد نوع البيانات المرجعة صراحة في نهاية سطر الترويسة باستخدام العبارة النحوية As. وتتخذ الصياغة العامة الشكل التالي:
Public Function CalculateInterest(ByVal Principal As Double, ByVal Rate As Double) As Double
يوفر هذا الإعلان الصريح ضماناً لما يُعرف بالأمان النوعي (Type Safety) في هندسة البرمجيات؛ حيث يفرض المترجم قيوداً صارمة أثناء التجميع والتشغيل تمنع إسناد بيانات غير متوافقة لاسم الدالة، مما يحمي النظام من أخطاء عدم توافق الأنواع البرمجية (Type Mismatch – خطأ وقت التشغيل رقم 13). وفي حال إغفال العبارة As DataType في نهاية الترويسة، تفترض بيئة تشغيل VBA تلقائياً أن الدالة تعيد النوع العام غير المقيد Variant، وهو سلوك يستهلك مساحات إضافية في الذاكرة ويقلل من سرعة المعالجة الحسابية.
3.2 الأنواع الأساسية للقيم المرجعة (Primitive Data Types)
تدعم لغة VBA باقة متكاملة من أنواع البيانات الأساسية التي يمكن للدوال احتسابها وإرجاعها، ويتطلب الاختيار بينها فهماً دقيقاً للمتطلبات الرياضية وحدود الذاكرة:
- الأنواع العددية الصحيحة: تشمل النوع
Integer(المخصص للأرقام بين -32,768 و 32,767 ويشغل بايتين) والنوع الأكثر أماناً واستخداماً في البيئات الحديثةLong(المخصص للأعداد الصحيحة الكبيرة ويشغل 4 بايت)، وتمنع هذه الأنواع حدوث أخطاء التقريب في عمليات العد والفهرسة. - الأنواع العشرية العائمة والمالية: يمثل النوع
Doubleالمعيار الذهبي للحسابات العلمية والهندسية عالية الدقة بفضل قدرته على استيعاب أرقام عشرية ضخمة عبر 8 بايت، بينما يبرز النوعCurrencyكخيار أمثل في الأنظمة المالية لمنع أخطاء تقريب الفواصل العشرية الحساسة. - البيانات النصية (String): تستخدم لإرجاع النصوص وسلاسل المحارف المتغيرة الطول، وتخضع لإدارة ذاكرة ديناميكية تتيح إرجاع رسائل نصية أو نصوص مدمجة بدقة وسلاسة.
- البيانات المنطقية (Boolean): تشغل بايتين وتقتصر مخرجاتها على
TrueأوFalse، وتعتبر أساسية في دوال التحقق من الشروط وصحة المدخلات.
3.3 النوع المتغير (Variant) واستخداماته في النتائج المرنة
يعد النوع المتغير Variant أضخم أنواع البيانات في VBA وأكثرها مرونة على الإطلاق؛ إذ يشغل 16 بايت في أنظمة 32-بت و24 بايت في أنظمة 64-بت، ويمتلك القدرة الاستثنائية على احتواء أي نوع بياني آخر، بدءاً من الأرقام والنصوص، ووصولاً إلى المصفوفات البرمجية والكائنات البرمجية المعقدة وحالات الأخطاء. على الرغم من أن الاعتماد المفرط عليه يعتبر مؤشراً على ضعف التصميم الهيكلي وهدراً لموارد الذاكرة، إلا أن هناك سيناريوهات برمجية حتمية تفرض استخدامه.
تتجلى أهمية إرجاع النوع Variant في المواقف التي تتطلب من الدالة مرونة مطلقة في تحديد نوع النتيجة وفقاً لظروف التنفيذ؛ كأن تعيد رقماً عشرياً في حال نجاح العملية الحسابية، أو تعيد كود خطأ مخصصاً باستخدام الدالة النظامية CVErr(xlErrDiv0) عند تعثر الحساب، لتقوم برمجية Excel بعرض الخطأ مباشرة في شبكة الخلايا بشكل مطابق تماماً للأخطاء الأصلية للتطبيق.
4. المعالجة الشرطية وتعدد مسارات التعيين (Conditional Logic)
4.1 توظيف تراكيب If…Then…Else لتحديد المخرجات
تعتبر الجمل الشرطية التفريعية If…Then…Else الأداة الأكثر شيوعاً في صياغة منطق الدوال الذكية؛ حيث تمكن المطور من توجيه مسار احتساب النتيجة استناداً إلى تقييم شروط منطقية متعددة. في هذا السياق، يقوم المطور بإسناد قيم متمايزة لاسم الدالة داخل الكتل الشرطية المختلفة تبعاً لحالة المعطيات المدخلة.
يستلزم التصميم البرمجي الآمن ضمان تغطية شاملة لكافة المسارات المتوقعة وغير المتوقعة؛ فالإخفاق في كتابة فرع Else ختامي في شجرة الشروط المعقدة قد يترك الدالة دون إسناد صريح في حال ورود مدخلات لم تكن متوقعة، مما يؤدي إلى خروج الدالة بقيمتها الافتراضية الصفرية أو الفارغة وإحداث خلل في الأنظمة المرتبطة. كما يمكن استغلال المسارات الشرطية لإجراء تعديلات متراكمة على اسم الدالة، بحيث يتم تعيين قيمة أساسية في البداية ثم تحديثها بزيادات متتالية كلما تحقق شرط جزئي معين.
4.2 التعامل مع الحالات الاستثنائية: نموذج القسمة على صفر
تعد الحالات الرياضية الحدية، وعلى رأسها محاولة القسمة على الصفر الحسابي، من أبرز التحديات التي توضح أهمية استخدام المعالجة الشرطية المسبقة لتعيين المخرجات المرجعة. فبدلاً من ترك المعالج ينفذ العملية بصورة عمياء مما يفجر خطأ وقت التشغيل القاتل (Division by zero – خطأ 11)، يقوم المبرمج باعتراض قيمة المقام مسبقاً قبل الشروع في أي خطوة حسابية.
في مثل هذه السيناريوهات، يمكن للدالة أن تتصرف بطرق احترافية متعددة؛ فإما أن يتم إسناد القيمة الصفرية للدالة كحل دفاعي مع الخروج الفوري، أو إسناد قيمة نصية توضيحية تحذر المستخدم من استحالة العملية إذا كان نوع الدالة يسمح بذلك (مثل الدوال المصرّح بها كـ Variant)، أو إرجاع ثابت خطأ مهيكل. تبرز هذه المعالجة الاستباقية الفارق الشاسع بين البرمجيات الهشة سريعة الانهيار وتلك المطورة وفق معايير الجودة والاستقرار الهندسي.
4.3 استخدام Select Case في إدارة المخرجات متعددة الحالات
عندما يتسع نطاق الخيارات والمسارات وتتعدد الشروط المبنية على تقييم متغير أو تعبير محدد، يصبح استخدام تركيبة Select Case خياراً متفوقاً من الناحيتين الهندسية والمقروئية مقارنة بكتل الشروط المتداخلة (Nested Ifs). تتيح هذه التركيبة فحص القيمة المدخلة ومقارنتها بسلسلة من الحالات المنتظمة، وتعيين القيمة المرجعة المناسبة لاسم الدالة داخل كل كتلة على حدة بصورة بالغة الوضوح.
تتجلى رصانة هذا النمط البرمجي في الإلزام المعياري بتضمين الكتلة الختامية Case Else؛ والتي تعمل كشبكة أمان تضمن أن الدالة ستقوم بإسناد قيمة مرجعة صالحة ومحددة حتى لو انحرفت المدخلات عن النطاقات المتوقعة ضمن الحالات الفردية السابقة، مما يوفر بيئة تنفيذية خالية من المخرجات الغامضة ويسهل عمليات التدقيق البرمجي.
5. نماذج تطبيقية تفصيلية لعمليات الحساب والإرجاع
5.1 دراسة حالة: دالة القسمة الرياضية DivideValues
لترسيخ المفاهيم المتقدمة للتعيين الاسمي والمعالجة الشرطية، نستعرض تحليلاً كودياً دقيقاً لدالة رياضية تتولى قسمة قيمتين عشريتين مع حماية النظام من أخطاء الانهيار الحسابي. يوضح النموذج التالي الصياغة النحوية المحكمة في بيئة VBA:
Public Function DivideValues(ByVal Numerator As Double, ByVal Denominator As Double) As Variant
If Denominator = 0 Then
DivideValues = "خطأ: لا يمكن القسمة على صفر"
Else
DivideValues = Numerator / Denominator
End If
End Function
يظهر التحليل الكودي لهذه الدالة اختيار النوع Variant للقيمة المرجعة، وذلك لمنح الدالة المرونة الكافية لإرجاع ناتج حسابي رقمي نقي من نوع Double في حالة المسار الطبيعي، أو إرجاع رسالة نصية تحذيرية من نوع String في حال تحقق شرط الصفر في المقام. تم في كلا المسارين إسناد النتيجة مباشرة لاسم الدالة DivideValues، مما يلبي قاعدة التعيين الاسمي الصريح ويضمن تسليم النتيجة السليمة إلى الخلية أو الروتين المستدعي دون أية أخطاء تنفيذية.
5.2 دالة مركبة لحساب الضرائب والخصومات المالية
في التطبيقات المصرفية والإدارية، تتطلب العمليات الحسابية التعامل مع شرائح متصاعدة تخضع لقواعد نسبية متفاوتة. يبرز المثال التالي كيفية بناء دالة متخصصة في احتساب الضريبة المستحقة على الدخل السنوي، مع توظيف آليات التعيين التدريجي للقيمة المرجعة:
Public Function CalculateTax(ByVal AnnualIncome As Double) As Double
Dim NetTax As Double
Select Case AnnualIncome
Case Is <= 10000
NetTax = 0
Case 10001 To 50000
NetTax = (AnnualIncome - 10000) * 0.1
Case Is > 50000
NetTax = (40000 * 0.1) + ((AnnualIncome - 50000) * 0.2)
Case Else
NetTax = 0
End Select
CalculateTax = NetTax
End Function
في هذا النموذج الهندسي، اعتمدت الدالة على متغير وسيط محلي لتجميع واحتساب القيمة الضريبية بدقة عبر فروع Select Case المتعددة، ثم قامت في السطر النهائي الموحد بإسناد القيمة النهائية المكتملة لاسم الدالة CalculateTax. يحقق هذا النمط وضوحاً فائقاً في تتبع مسار البيانات أثناء مرحلة التنقيح، ويجنب المطور تشتيت تعيينات اسم الدالة في مواضع متفرقة يصعب ضبطها عند التوسعات البرمجية المستقبلية.

5.3 دالة معالجة النصوص والتحقق من صحة البيانات المدخلة
تتعدى استخدامات الدوال المرجعة العمليات الحسابية الصرفة لتمتد إلى عمليات تدقيق النصوص والتحقق من مطابقة المدخلات للمعايير الشكلية والتنظيمية. يوضح الكود التالي دالة تقوم بفحص تنسيق البريد الإلكتروني والتأكد من احتوائه على الرموز الأساسية:
Public Function IsValidEmailSimple(ByVal EmailAddress As String) As Boolean
Dim AtIndex As Long
Dim DotIndex As Long
AtIndex = InStr(1, EmailAddress, "@")
DotIndex = InStrRev(EmailAddress, ".")
If AtIndex > 1 And DotIndex > AtIndex + 1 And DotIndex < Len(EmailAddress) Then
IsValidEmailSimple = True
Else
IsValidEmailSimple = False
End If
End Function
تستقبل هذه الدالة المعامل النصي وتجري فحوصاً موضعية باستخدام دوال السلاسل النصية المدمجة في VBA، ثم تسند ناتج التحقق المنطقي الصريح (إما True في حالة التوافق الهيكلي أو False في حالة الخلل) لاسم الدالة مباشرة. وتعمل مثل هذه الدوال المرجعة كوحدات بناء أساسية لتوجيه مسار الإجراءات التنفيذية الكبرى؛ حيث يمكن وضع استدعاء الدالة مباشرة ضمن شرط إجرائي مثل If IsValidEmailSimple(UserEntry) Then ProcessRecord لضمان سلامة العمليات التراكمية في قواعد البيانات.
6. إرجاع البيانات المتقدمة: المصفوفات والكائنات (Arrays and Objects)
6.1 آليات إرجاع مصفوفة أحادية أو متعددة الأبعاد من الدالة
لا تقتصر قدرات دوال VBA على إعادة القيم البسيطة المفردة، بل تمتد لتشمل إرجاع هياكل بيانات مجمعة بالكامل عبر المصفوفات (Arrays). للإعلان عن دالة تعيد مصفوفة أحادية أو متعددة الأبعاد، يتم تضمين زوج من الأقواس الفارغة بعد نوع البيانات المصرّح به في ترويسة الدالة، مثل Function GetDaysOfWeek() As String() أو تصريحها كنوع عام Variant يحتوي على مصفوفة.
يتم البناء الداخلي للدالة عبر تهيئة وتعبئة مصفوفة محلية ديناميكية أو ثابتة، ثم إسناد المصفوفة بالكامل دفعة واحدة إلى اسم الدالة. كما تتيح الدوال المدمجة المتطورة مثل Split إنشاء وإرجاع مصفوفة نصية فائقة السرعة من خلال تقسيم نص مفرد استناداً إلى فاصل محدد، وإسناد مصفوفة النتائج مباشرة للدالة، مما يسهل معالجة ملفات السجلات والبيانات المفصولة بفواصل (CSV) بكفاءة معمارية عالية.
6.2 إرجاع الكائنات باستخدام الكلمة المفتاحية Set
عند الرغبة في إرجاع كائن برمجي (Object Reference) يمثل عنصراً حياً ضمن شجرة نماذج كائنات التطبيق (مثل كائن ورقة العمل Worksheet، أو نطاق الخلايا Range، أو كائن مصنف العمل Workbook)، تتغير القاعدة النحوية الصارمة لعملية الإسناد؛ إذ يحظر استخدام معامل الإسناد البسيط بمفرده، ويلتزم المطور وجوباً بتصدير سطر التعيين بالكلمة المفتاحية Set.
يرجع هذا الإلزام النحوي إلى طبيعة معمارية الذاكرة؛ فالإسناد البسيط يتعامل مع قيم البيانات الفعلية المخزنة مباشرة، في حين أن التعامل مع الكائنات يتطلب ربط مؤشر مرجعي (Pointer Reference) يشير إلى موقع الكائن في الذاكرة العشوائية. يتخذ الإسناد في هذه الحالة الصيغة الإلزامية التالية:
Public Function GetSummaryCell(ByVal TargetSheet As Worksheet) As Range
Set GetSummaryCell = TargetSheet.Range("A1")
End Function
إن إغفال الكلمة المفتاحية Set عند إرجاع الكائنات يتسبب فوراً في إطلاق خطأ وقت التشغيل الشهير (Object variable or With block variable not set – خطأ 91)، مما يؤكد الضرورة المعمارية للتمييز الدقيق بين إرجاع القيم المادية البسيطة وإرجاع المراجع الكائنية في لغة VBA.
6.3 إرجاع هياكل البيانات المخصصة (User-Defined Types – UDT)
توفر لغة VBA للمطورين القدرة على إنشاء هياكل بيانات تركيبية تجمع بين حقول وعناصر متعددة ومختلفة الأنواع تحت مسمى هيكلي موحد باستخدام الكلمة المفتاحية Type في القسم العام للوحدة النمطية القياسية، وهو ما يعرف بالأنماط المعرفة من قبل المستخدم (UDTs). وتتيح اللغة للدوال إرجاع هذه السجلات المركبة بالكامل لتسهيل نقل البيانات المترابطة دفعة واحدة.
Public Type EmployeeRecord
ID As Long
FullName As String
BaseSalary As Double
End Type
Public Function CreateEmployee(ByVal empID As Long, ByVal empName As String, ByVal salary As Double) As EmployeeRecord
Dim tempEmp As EmployeeRecord
tempEmp.ID = empID
tempEmp.FullName = empName
tempEmp.BaseSalary = salary
CreateEmployee = tempEmp
End Function
يتم استدعاء هذه الدالة في الروتين الخارجي واستقبال مخرجاتها في متغير من النوع المخصص ذاته، مما يتيح الوصول إلى الحقول الفرعية بسهولة تامة (مثل CurrentWorker.FullName)، ويمثل هذا النمط بديلاً خفيف الوزن للفئات البرمجية (Class Modules) عندما يكون الهدف مجرد تغليف ونقل حزم البيانات المتجانسة.
7. التحكم في سير التنفيذ والخروج المبكر (Exit Function)
7.1 دور التعليمة Exit Function في تحسين تدفق الكود
تؤدي التعليمة البرمجية Exit Function دوراً محورياً في إدارة التدفق المنطقي وتفادي التعقيد الهيكلي في الأكواد؛ إذ توفر آلية فورية وحاسمة لإنهاء عمل الدالة وتجاوز كافة الأسطر اللاحقة بمجرد استيفاء شرط معين أو الوصول إلى النتيجة المطلوبة. يساعد هذا الأسلوب المطورين على تفادي “نمط الشروط المتداخلة العميقة” (Deeply Nested Ifs) التي تجعل الكود معتماً وصعب القراءة والصيانة.
من منظور الأداء الحسابي، يعمل الخروج المبكر على تسريع زمن الاستجابة؛ فبمجرد التحقق من عدم صلاحية المعاملات المدخلة أو العثور على العنصر المستهدف في مصفوفة ضخمة، يتم قطع المعالجة فوراً والعودة بالنتيجة، بدلاً من إهدار دورات المعالج في المرور العقيم على بقية جسم الدالة غير الضروري.
7.2 ضمان تثبيت القيمة المرجعة قبل أمر الخروج
ينطوي استخدام أمر الخروج المبكر على مخاطرة برمجية كبرى تتعلق بسلامة البيانات؛ حيث يقع العديد من المطورين في فخ استدعاء Exit Function قبل إسناد القيمة المطلوبة لاسم الدالة. وبما أن الخروج المبكر يقفز فوراً إلى ما بعد End Function متجاوزاً أي تعليمات لاحقة، فإن الدالة في هذه الحالة ستعود بالنتيجة الافتراضية للنوع البياني (كالصفر أو السلسلة الفارغة)، مما يقود إلى تشوهات في المخرجات.
لتفادي هذا الخلل، تفرض هندسة البرمجيات الدفاعية الالتزام بترتيب زمني صارم: إسناد القيمة المستهدفة لاسم الدالة أولاً في سطر مستقل، يليه مباشرة في السطر التالي إطلاق أمر Exit Function. يضمن هذا النمط المحكم تثبيت القيمة المرجعة في وعاء الدالة قبل إغلاق الإطار التنفيذي ومغادرته.
7.3 المقارنة التحليلية بين Exit Function والوصول إلى End Function
يثور في الأوساط الأكاديمية والمهنية نقاش مستمر حول أفضلية نمط “نقطة الخروج الموحدة” (Single Exit Point) المعتمد فقط على الوصول الطبيعي إلى End Function، مقابل نمط “نقاط الخروج المتعددة” (Multiple Exit Points) المعتمد على تكرار استخدام Exit Function:
- نقطة الخروج الموحدة (End Function): تتميز بتسهيل عمليات التتبع والتدقيق البرمجي وضمان تنفيذ عمليات التنظيف البرمجي وتحرير الموارد في موقع ختامي موحد قبل مغادرة النطاق، إلا أنها قد تفرض تداخلاً شرطياً معقداً للتحكم في الوصول إلى النهاية.
- نقاط الخروج المتعددة (Exit Function): تحقق وضوحاً فائقاً في معالجة الحالات الحدية والشروط الدفاعية في مقدمة الدالة (Guard Clauses)، وتقلل من الهياكل المتداخلة، لكنها تتطلب يقظة تامة لضمان تثبيت القيمة المرجعة وتحرير الذاكرة في كل مسار خروج على حدة.
8. معالجة الأخطاء الاستثنائية وضمان موثوقية القيم المرجعة
8.1 تضمين كتل معالجة الأخطاء (On Error GoTo)
تعد الدوال المعرضة للانهيار البرمجي المفاجئ تهديداً خطيراً لسلامة النظم المؤتمتة، ولذلك يتعين تحصين الدوال الحساسة بآليات معالجة استثنائية متطورة باستخدام العبارة التوجيهية On Error GoTo. تتيح هذه التعليمة عزل الأخطاء غير المتوقعة ونقل مسار التنفيذ فوراً إلى قسم فرعي مخصص لمعالجة الخطأ أسفل الدالة، بدلاً من إيقاف البرنامج وظهور رسائل الخطأ المربكة للمستخدم النهائي.
يجب الانتباه بشدة عند استخدام التعليمة البديلة On Error Resume Next؛ فرغم أنها تتجاهل الخطأ وتستمر في السطر التالي، إلا أنها قد تتسبب في تجاوز سطر التعيين الحرج لاسم الدالة، مما يجعل الدالة ترجع قيماً خاطئة دون علم المطور. ولذا، يقتصر استخدامها على نطاقات ضيقة ومحكومة بالتحقق الفوري من حالة الكائن Err.Number.
8.2 إسناد قيم أمان بديلة عند تعطل المعالجة (Fallback Values)
عندما يتعثر الروتين الحسابي للدالة وينتقل التنفيذ إلى كتلة معالجة الأخطاء، تبرز ضرورة إسناد “قيمة أمان بديلة” (Fallback Value) متفق عليها هندسياً لاسم الدالة، تضمن استمرار التطبيق المستدعي في عمله دون انهيار متسلسل:
Public Function ReadConfigurationValue(ByVal SettingKey As String) As Long
On Error GoTo ErrorHandler
' كود قراءة الإعدادات المعرض للفشل...
Exit Function
ErrorHandler:
ReadConfigurationValue = -1 ' قيمة أمان تعبر عن الفشل التقني
End Function
يتيح هذا التعيين الدفاعي للروتين المستدعي فحص القيمة المرجعة، فإذا كانت تمثل قيمة الفشل الاصطلاحية (مثل -1 أو نص محدد أو قيمة CVErr)، يمكنه اتخاذ الإجراء التصحيحي اللازم وتدوين الخطأ في سجل النظام دون انقطاع سير العمل المؤسسي.
8.3 التحقق القبلي من المدخلات لمنع حدوث الأخطاء مسبقاً
تعتبر البرمجة الاستباقية القائمة على فحص المدخلات قبل معالجتها أكثر كفاءة وأمناً من الاعتماد المطلق على مصائد الأخطاء التفاعلية. تتضمن هذه الممارسة التحقق الصارم من حالة المعاملات المدخلة عبر الدوال الفاحصة المدمجة في VBA، مثل التأكد من رقمية البيانات عبر IsNumeric، أو صحة التواريخ عبر IsDate، أو خلو المتغيرات من الحالات الشاذة عبر IsEmpty وIsNull.
بمجرد اكتشاف مدخل غير صالح، تقوم الدالة فوراً بإسناد القيمة التنبيهية أو الافتراضية لاسمها وتنهي التنفيذ عبر Exit Function في الأسطر الأولى من الكود، وهو ما يحافظ على نقاء المنطق الداخلي للدالة ويحمي دورات المعالجة من العمليات الفاشلة مسبقاً.
9. استخدام الدوال المرجعة في بيئة Excel: الدوال المخصصة (UDF)
9.1 استدعاء دالة VBA المخصصة مباشرة من خلايا أوراق العمل
تكتسب دوال VBA المرجعة قيمة تطبيقية هائلة بفضل قدرتها على العمل كـ “دوال معرفة بواسطة المستخدم” (User-Defined Functions – UDFs) داخل بيئة الجداول الإلكترونية لمايكروسوفت إكسيل؛ حيث يمكن إدراج الدالة مباشرة في شريط المعادلات داخل أي خلية مثل: =DivideValues(A2, B2). لتأهيل الدالة للظهور والاستخدام السلس داخل أوراق العمل، يشترط أن تكون الدالة معلنة بالصفة العامة Public ومحفوظة داخل “وحدة نمطية قياسية” (Standard Module) وليست داخل وحدة تابعة لورقة أو مصنف محدد.
تخضع الدوال المخصصة لقيود تشغيلية صارمة يفرضها محرك إكسيل أثناء حساب أوراق العمل؛ فالوظيفة الحصرية المسموح بها للدالة هي “احتساب وإرجاع قيمة إلى الخلية الحاضنة لها فقط”. ويحظر تماماً على أي دالة مستدعاة من ورقة العمل تعديل محتويات خلايا أخرى، أو تغيير تنسيقات الألوان والخطوط، أو إضافة وحذف أوراق العمل، حيث يؤدي انتهاك هذه الضوابط الأمنية إلى فشل الدالة وإرجاع الخطأ الشهير #VALUE!. وتكتسب هذه التطبيقات أهمية كبرى في أطر العمل التعليمية والنمذجة المحاكية لإدارة المشروعات، كما يوضح الباحث وي-ليونغ لي (Wee-Leong Lee, 2011) في دراسته حول توظيف بيئات الجداول الإلكترونية لمحاكاة العمليات المعقدة.

9.2 التحديث التلقائي والدوال المتطايرة (Volatile Functions)
يعتمد محرك الجداول الإلكترونية على تقنية “شجرة الاعتماديات الحسابية” (Calculation Dependency Tree) لتحديث الخلايا؛ حيث لا يعيد حساب الدالة المخصصة إلا إذا تغيرت قيم الخلايا المشار إليها كمعاملات مدخلة في صيغة الاستدعاء. ومع ذلك، تتطلب بعض الدوال إعادة احتساب دائمة ومستمرة حتى وإن لم تتغير مدخلاتها (مثل الدوال التي تعتمد على الوقت الحالي أو حركة المؤشر).
لتمكين هذا السلوك، يوفر كائن التطبيق التعليمة Application.Volatile True في السطر الأول من الدالة. تحول هذه التعليمة الدالة إلى “دالة متطايرة”؛ مما يجبر إكسيل على إعادة حساب قيمتها المرجعة مع كل عملية حسابية تجري في أي خلية داخل المصنف بأكمله. يتطلب استخدام هذه الميزة حذراً هندسياً شديداً؛ فالإفراط في استخدام الدوال المتطايرة يؤدي إلى استنزاف حاد لطاقة وحدة المعالجة المركزية (CPU) وبطء ملحوظ ومستمر في أداء المصنفات المالية الضخمة.
9.3 مقارنة استدعاء الدالة داخل VBA مقابل استدعائها من واجهة Excel
يخضع استقبال القيمة المرجعة لآليات متباينة تبعاً للبيئة التي انطلق منها الاستدعاء:
- الاستدعاء الداخلي ضمن بيئة VBA: يتم استقبال النتيجة وتعيينها لمتغير مخصص مباشرة مثل:
Total = CalculateTax(GrossPay). تحتفظ النتيجة بخصائصها النوعية الدقيقة في الذاكرة (مثل نوع التاريخDateأو الرقم العشريDouble)، وتتاح للمطور القدرة الكاملة على فحص الكائنات والمصفوفات والتصرف برمجياً استناداً إلى حالتها. - الاستدعاء عبر شبكة خلايا Excel: تمر القيمة المرجعة بمرحلة وسيطة لتحويل البيانات إلى ما يناسب شبكة الخلايا؛ حيث تترجم القيم النصية والرقمية والمنطقية بدقة، بينما تترجم حالات الخطأ البرمجي المرجعة عبر
CVErrإلى الرموز المقابلة في واجهة المستخدم (مثل#DIV/0!أو#N/A)، ويتم تجاهل أو تسطيح الكائنات والمصفوفات المتشعبة ما لم تكن النسخة تدعم الدوال الديناميكية الحديثة للمصفوفات (Dynamic Array Formulas).
10. أفضل الممارسات البرمجية والأداء الأمثل لدوال إرجاع القيم
10.1 قواعد التسمية النظيفة وتوثيق الغرض من القيمة المرجعة
تمثل مقروئية الكود معياراً رئيسياً لجودته في البيئات البرمجية الاحترافية. يجب أن تعكس أسماء الدوال بوضوح الإجراء الحسابي وطبيعة القيمة المرجعة المتوقعة؛ ويفضل دمج الأفعال والأسماء بصيغة سنام الجمل الموحدة (CamelCase أو PascalCase)، مثل CalculateNetProfit أو GetCustomerStatus. وفي الدوال التي تعيد قيماً منطقية، يحبذ تصدير الاسم بأدوات الاستفهام البرمجية المتعارف عليها مثل Is... أو Has... (مثل IsInvoicePaid).
يتعين على المطور تضمين ترويسة توثيقية وافية تعلو جسم الدالة مباشرة، تشرح بإيجاز الغرض من الدالة، وطبيعة كل معامل مدخل، ونوع البيانات المرجع بالتفصيل وما تدل عليه القيم الخاصة (مثل القيم السالبة أو الصفرية)، مما يقلل بشكل ملموس من الوقت المستغرق في فهم الكود وصيانته بواسطة فرق التطوير اللاحقة.
10.2 تحسين الأداء وتقليل استهلاك موارد الذاكرة
للوصول إلى أعلى كفاءة تنفيذية ممكنة، يجب مراعاة الممارسات التقنية التي تقلل من إجهاد مكدس الذاكرة ودورات المعالج:
- التخلي عن النوع العام Variant: يجب حصر استخدام Variant في أضيق الحدود والتصريح الصارم بالأنواع الثابتة (مثل
Longبدلاً منVariant) لتفادي استهلاك 16 بايت لكل متغير ولتجنب عمليات التحويل الضمني المتكررة للأنواع أثناء الحساب. - التقليل من المتغيرات الوسيطة الزائدة: في العمليات الحسابية البسيطة، يمكن إسناد التعبير الرياضي لاسم الدالة مباشرة دون الحاجة لإنشاء متغير محلي إضافي ونقل القيمة عبر وسائط متعددة.
- تمرير المعاملات بالقيمة أو المرجع بحكمة: يساعد استخدام
ByValفي حماية البيانات الأصلية ولكنه ينسخ المتغير في الذاكرة، بينما يعتبرByRefأسرع في التعامل مع الهياكل الضخمة والمصفوفات دون نسخها، مما يحسن سرعة استجابة الدالة المرجعة. - تعطيل تحديث الشاشة في الدوال الإجرائية الكثيفة: إذا كانت الدالة تعتمد على استدعاءات إجرائية فرعية متعددة، يمكن للتعليمة
Application.ScreenUpdating = Falseتوفير أزمنة تنفيذ هائلة.
10.3 الفحص المنهجي وتصحيح الأخطاء (Debugging)
تعتبر مهارات تصحيح الأخطاء والتنقيح ضرورة حتمية لضمان وصول الدالة للنتيجة الدقيقة المتوقعة. توفر بيئة التطوير المتكاملة (VBA IDE) أدوات متقدمة لفحص آليات إرجاع القيم:
- نقاط التوقف البرمجية (Breakpoints): يتيح تعيين نقطة توقف عبر الضغط على المفتاح
F9إيقاف التنفيذ عند سطر إسناد القيمة لاسم الدالة، مما يمكن المطور من وضع مؤشر الفأرة فوق اسم الدالة للتأكد من استقرار القيمة المرجعة الصحيحة بداخلها قبل الخروج. - نافذة المراقبة الفورية (Immediate Window): تمثل المنصة الأسرع لاختبار مخرجات الدوال دون الحاجة لبناء إجراءات فرعية كاملة؛ حيث يكفي كتابة علامة الاستفهام متبوعة باسم الدالة ومعاملاتها مثل:
? DivideValues(10, 2)والضغط على زر الإدخال لتلقي النتيجة المرجعة فوراً على الشاشة وفحص سلوكها في مواجهة الحالات الحدية المتنوعة.
خاتمة
يمثل إرجاع القيم من الدوال في لغة برمجة التطبيقات للمكاتب (VBA) الركيزة التقنية الكبرى لبناء برمجيات مكتبية تتسم بالقوة والمرونة وقابلية الصيانة العالية. إن الآلية الفريدة التي تنتهجها اللغة في معاملة اسم الدالة كمتغير محلي حامل للقيمة المرجعة تتطلب من المطورين إدراكاً عميقاً لمفاهيم الأمان النوعي، ومسارات التدفق الشرطي، وإدارة الذاكرة أثناء التعامل مع مختلف الأنماط البيانية البسيطة والمعقدة كالكائنات والمصفوفات.
من خلال الالتزام بالمعايير الهندسية الصارمة المتمثلة في التصريح الواضح عن الأنواع بواسطة العبارة As DataType، والتأكد من تثبيت النتيجة بدقة قبل استدعاء أمر الخروج المبكر Exit Function، وتدعيم الأكواد بمصائد استباقية للأخطاء وخطط أمان بديلة، يستطيع المطور تحويل برمجيات الجداول الإلكترونية إلى منصات حسابية موثوقة تلبي متطلبات الأعمال المعقدة بدقة متناهية وأداء حاسم.
المراجع
- Lee, W.-L. (2011). Spreadsheet based experiential learning environment for project management. Winter Simulation Conference. https://doi.org/10.1109/wsc.2011.6148079