سلسلة من المقالات، احاول فيها تحفيز مبرمجي Visual Basic x.0 العرب الى مواكبة التيار والهجرة بلا عودة الى احد لغات اطار عمل NET Framework. أردت هنا اكمال السلسلة والتحدث عن الفئات Classes والكائنات Object وطريقة التعامل معها في VB6: 1) لا يمكنك ارسال الخصائص بالمرجع ByRef عندما تعرف الخصائص Properties في فئاتك، فستقوم بكتابتها على شكل اجراءات Property Get و Property Let، والهدق منها محاكاة المتغيرات التقليدية حتى تتمكن من اسناد قيمها: Dim X As New TestClass X.TestProperty = 100 المشكلة ان VB6 يطبق ما يسمى التقليد الأعمى، حيث ان الخاصية TestProperty السابقة لايمكنك ارسالها بالمرجع ByRef الى الاجراءات –سواء Subs او Functions. قد يأتي احد مبرمجي المتمرسين ويقترح علي تعريف الخصائص على شكل متغيرات في الفئات: ' TestClass في الملف Public TestProperty As Integer لقد وقعت في الفخ يا صديقي العزيز! وذلك ان VB6 لغة الحالات الشاذة، فالشيفرة السابقة قد قامت بتعريف الإجراءات Property Let و Property Set مخفية (حتى يتم التوافق مع COM)، مما يعني انك لن تستطيع ارسال قيم الخصائص بالمرجع. ولكن مع VB.NET فهو يقدم فكرة الحقول Fields التي تمكنك من ارسال القيم بالمرجع، ليس هذا فقط بل حتى الخصائص يمكن ارسالها بالمرجع. 2) المشيدات لا تقبل وسيطات اثبت VB6 قوة تطبيق مبدأ التغليف Encapsulation في فئاته حيث يوفر امكانية تعريف المشيدات Constructors في الفئات. ولكن مع الاسف الشديد لا يمكنك اضافة الوسيطات الهامة Parameters في المشيدات حتى تحمي فئاتك من الاخطاء، فلو وجدت خصائص ضرورية عليك كتابة معروض الى مستخدم الفئة وكتابة كافة قصائد المدح والرجاء حتى يوافق ويتذكر اسناد قيم هذه الخصائص بعد انشاء الكائن. اما مع VB.NET فكل ما هو مطلوب منك تعريف الوسطيات المطلوبة في مشيدها: ' *** VB.NET ********** Class TestClass Sub New (x As Integer, Y As String) … End Sub End Class 3) اعادة التعريف اعادة التعريف Overloading غير مدعومة في VB6، مما يعني كتابة عشرات الجمل الشرطية قبل استدعاء الطرق، وذلك حتى يتم التحقق من النوع المناسب لوسيطات الطريقة: ' *** VB6 ********** If VarType(x) = 20 Then ' String MyObject.OpenStringMethod(x) ElseIf VarType(x) = 2 Then ' Integer MyObject.OpenIntegerMethod(x) ElseIf … Then ... ElseIf … Then … End IF الشيفرة السابقة تفترض ان الطريقة تستقبل وسيطة واحدة، ولكن تخيل انها تستقبل اكثر من وسيطة: ' *** VB6 ********** If VarType(x) = 20 And VarType(y) = 2 Then MyObject.StringIntegerMethod(x, y) ElseIf VarType(x) = 20 And VarType(y) = 20 MyObject.StringStringMethod(x, y) ElseIf VarType(x) = 2 And VarType(y) = 20 MyObject.IntegerStringMethod(x, y) ElseIf VarType(x) = 2 And VarType(y) = 2 MyObject.IntegerIntegerMethod(x, y) … End IF لا تعليق !!! 4) إجراءات الاحداث تعرف وقت التصميم فقط عندما تنوي تعريف اجراء ليقنص حدث ما، فهذا الاجراء سيتم تعريفه وقت التصميم Design Time فقط، ولايمكن اجراء عملية القنص وقت التنفيذ Run Time، وذلك بسبب الصيغة الغريبة التي يتبعها VB6 لقنص الحدث: ' *** VB6 ********** Sub ObjectName_EventName () … End Sub ولكن مع VB.NET فهو يمكنك من تحديد الاجراء وقت التنفيذ باستخدام الامر AddHandler: ' *** VB.NET ********** AddHandler MyObject.Event, AddressOf (MySub) 5) المتغيرات العامة Public لابد ان تكون متوافقة مع COM فالمتغيرات الحرفية ثابتة الطول Fixed-Length Strings والمصفوفات Arrays لايمكن ان تكون Public. 6) تحتاج الى ساحر لتطبيق حلقة For Each على فئات المجموعات قمت بعد سهر وتعب بتطوير فئة مجموعة Collection Class، واردت ان تكون متوافقة مع مواصفات ومعايير COM وذلك بإعطائها قابلية استخدام الحلقة For Each معها. للاسف الشديد VB6 لا يمكنك من فعل ذلك، لذلك عليك التوغل في واجهات COM وتعريف اجراء من الواجهة IUnknown ليعود بالواجهة IEnumVariant والتي لن تستطيع الحصول عليها الا من كائن من النوع Collection (يدعم هذه الواجهة) وكتابة هذه الشيفرة الغريبة: ' *** VB6 ********** Public Property Get NewEnum() As IUnknown Set NewEnum = m_CollectionObject.[_NewEnum] End Property الم اقل لك انك تحتاج الى ساحر؟ ولكن مع VB.NET فهو يمكنك من تطبيقها دون اللجوء الى السحرة والمشعوذين، فيكفي تضمين واجهات مقدمة من اطار عمل .NET (واضحة المعنى والمضمون) في فئاتك لتدعم هذه الحلقة. 7) ماذا عن الوراثة؟ الوراثة Inheritance للاسف الشديد غير مدعومة، وعملية محاكاة الوراثة (باستخدام التفويض Delegation) اثبتت فشلها الذريع. لن اتحدث عن فائدة الوراثة وما تقدمه لك، وسأكتفي بقول: إلي ما يعرف الصقر يشويه! 8) اسناد قيم الكائنات (ضرورة Set) من الاشياء التي اثمرت في نمو الشوائب Bugs في برامج VB6 هي ضرورة استخدام Set عند الحديث عن عملية اسناد القيم بين الكائنات، اما ان تجاهلها المبرمج فلن تظهر رسالة خطأ، وذلك بسبب الخصائص الافتراضية Default Propeties التي يدعمها VB6، فالشيفرة التالية: ' *** VB6 ********** MyObject = YourObject لا نعلم هل تسبب خطأ وقت التنفيذ او لا. 9) فئات الانعكاس Reflection Classes لاتوجد طريقة واضحة وسهلة في VB6 تمكننا من معرفة جميع الفئات واعضائها التي تحتويها مكونات COM، وان اردت ذلك عليك اتقان لغة خاصة بـ COM (والتي تسمى Interface Definition Language – (IDL) تشبه الى حد كبير لغة C. ولكن مع VB.NET فيمكن معرفة كل شئ عن مكونات .NET عن طريقة مجموعة من الفئات تسمى فئات الانعكاس Reflection Classes واستخدامها يتم بنفس لغة البرمجة VB.NET وليس لغة اخرى. 10) الاحداث تعتمد على المؤشرات وليس الكائنات. من الاشياء المحزنة والمخزية في VB6 هو فلسلفة تطبيقه لعملية قنص الاحداث، حيث ان العبارة WithEvents تتبع المؤشر وليس الكائن: ' *** VB6 ********** Dim WithEvents X As TestClass ... ... Sub X_EventName() ... End Sub ان تم نسخ الكائن الى مؤشر اخر وتم قتل المؤشر الاصلي X فلن يتم تنفيذ الحدث EventName: ' *** VB6 ********** Dim Y As TestClass Y = X X = Nothing ولكن مع VB.NET فيمكنك قنص الاحداث استنادا الى كائناتها عوضا عن مؤشراتها. 11) حفظ بيانات الكائنات او نسخها (تسلسل الكائنات) لاتوجد ميكانيكية واضحة تمكن مبرمجي VB6 من حفظ بيانات الكائنات الموجودة في الذاكرة، ولا حتى نسخها من كائن الى كائن. وان قمت بعمل ذلك بشكل يدوي وكتابة عشرات الشيفرات المصدرية، فعليك تعديل هذه الشيفرة في كل مرة تضيف خاصية في الفئة. ليس هذا فقط، بل عليك اضافة شيفرات اضافية ايضا في كل مرة تعدل فيها المتغيرات الستاتيكية Static Variables في اجراءات الفئة. ولكن مع VB.NET فهو يمكنك من عمل هذا بفضل مجموعة من الفئات مقدمة من اطار عمل .NET تمكنك من نسخ الكائنات وحفظها وهو ما يمسى تسلسل الكائنات Object Serialization. 12) New لا تنشئ الكائن حتى يتم استدعاء احد طرقه الكلمة المحجوزة New تنشئ كائن جديد، في الحقيقة ليس دائما: ' *** VB6 ********** Dim obj As New TestClass عند استخدام New لحظة تعريف المتغير –كما بالشيفرة السابقة- فلن يتم انشاء الكائن حتى تستدعي احد اعضاءه. هذه الخزعبلات لن تجدها مع VB.NET. 13) المرجعية الدائرية Circular Reference من اكبر الصعاب التي واجهة المبرمجين مشكلة المرجعية الدائرية Circular Reference، والتي تبقي كائنين على قيد الحياة بسبب وجود مؤشر في كل كائن يشير الى الكائن الاخر: ' *** VB6 ********** ' Person.cls في الملف Public Name As String Public Brother As Person عن انشاء كائنات من هذه الفئة، واحداث مرجعية دائرية بينهما (عن طريق الخاصية Brother): ' *** VB6 ********** Dim X As New Person Dim Y As New Person X.Name = "عباس السريع" Y.Name = "عبود اللوح" Set X.Brother = Y Set Y.Brother = X فان الكائنين سيبقيان في الذاكرة حتى وان اختف مؤشراتهما، لا تصدق جرب واكتب شيئا مثل: Set X = Nothing Print Y.Brother.Name ' عباس السريع ولكن مع VB.NET فلن تحدث مشكلة المرجعية الدائرية ابدا، وذلك بفضل المجموعة Garbage Collection (GC) والتي بها ميكانيكية ذكية تزيل كافة الكائنات من الذاكرة والتي لا تشير لها مؤشرات في قابلة للوصول من الشيفرة المصدرية. كان هذا كل ما وددت ذكره حول الفئات والكائنات مع VB6، في المرة القادمة سنأخذ جولة سريع حول النماذج Forms ونرى قصور رمز وسبب نجاح VB6 (النماذج Forms). -- تركي