إني ذكرتك والذكرى مؤرقة 3/4

الناقل : elmasry | الكاتب الأصلى : تركي العسيري | المصدر : www.al-asiri.com

سلسلة من المقالات، احاول فيها تحفيز مبرمجي 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‏).‏

‏-- تركي‏