س: إذا كان النوع String فئة Class، فلماذا عندما نسند إليه قيمة نكتب مثلاً:
Dim X As String
= "Any Thing"
بدلاً من:
Dim X As New String("Any Thing")
وإذا كان النوع Integer هو Structure فلماذا نسند له أرقام مباشرة بدلاً من استخدام
الصيغة:
Dim I As New Integer(1000)
ج: الفئات Classes:
هي أنواع
مرجعية Reference Types..
هذا معناه أن الكائن Object الذي يتم تعريفه من الفئة هو مجرد متغير يحمل مرجعا Reference للذاكرة التي تحتوي على تفاصيل
هذا الكائن.. على سبيل المثال:
Dim F As Form
الكائن F في الكود السابق هو مجرد مخزن
يوضع فيه عنوان الذاكرة الذي يحتوي على كل بيانات النموذج.. ونظرا لأن الكود
السابق لم ينشئ أي نموذج، فإن المتغير F سيكون فارغا وقيمته Nothing، أي انه لا يشير إلى أي ذاكرة،
ولو حاول المبرمج استخدام المتغير F
في أي عملية في الكود، فسيحدث خطأ في البرنامج.. لهذا يجب استخدام الكلمة New لإنشاء كائن النموذج في الذاكرة،
ووضع عنوانه (مرجعه) في المتغير F:
F = New Form(
)
الآن، صار
المتغير F يشير إلى كان
النموذج الجديد، ويمكن استخدامه في الكود بشكل عادي لتغيير حجم النموذج أو لون
خلفيته أو عرضه... إلخ:
F.Show( )
ويمكن نسخ
المرجع الموجود في المتغير F
ووضعه في متغير آخر:
Dim F2 As
Form = F
في هذه الحالة
لا يتم إنشاء نموذج جديد، ولكن كل ما يحدث هو أن يحمل كل من المتغيرين F و F2 نفس العنوان (المرجع) الذي يشير
إلى نفس نسخة النموذج، وأي تغيير يحدث على أي متغير، يؤثر على المتغير الآخر،
لأنهما فعليا مجرد اسمين لنفس الكائن، وليسا كائنين مختلفين.
هذه هي القاعدة
العامة للتعامل مع جميع الفئات والكائنات المعرّفة منها.. لكن الأمر يختلف عند
التعامل مع الفئة String،
نظرا لأنها نوع أساسي يستخدم بكثرة في الكود، ومن المريح التسهيل على المستخدم عند
التعامل مع هذا النوع.. لهذا يوجد استثناءان عند التعامل مع النصوص:
1- لا يحتاج
المبرمج إلى استخدام الكلمة New
عند تعريف النص.. لهذا يمكن استخدام الصيغة:
Dim X As String
= "Any Thing"
وقد سبق أن
أوضحت في إجابة السؤال السابق أن فيجيوال بيزيك تترجم هذا الكود إلى اللغة الوسيطة
IL، لهذا لا يهم الصيغة
التي تستخدمها فيجيوال بيزيك وأي استثناءات خاصة تمنحها للمبرمج، ففي النهاية،
ستتم ترجمة كل ذلك إلى الصيغة القياسية الخاصة باللغة الوسيطة.
مع ملاحظة أن
الصيغة الأصلية أيضا متاحة وصحيحة:
Dim X As New String("Any Thing")
وكما قلت لك:
كلتا الصيغتين ستنتجان نفس الكود بالضبط في اللغة الوسيطة J
2- إسناد متغير
نصي إلى آخر يؤدي إلى نسخ القيمة وليس المرجع.. مثال:
Dim X As String = "A"
Dim Y As String = X
X = "B"
MsgBox(X) ' B
MsgBox(Y) ' A
ستعرض الرسالتان
قيمتين مختلفتين، لأن المتغيرين X
و Y يشيران إلى
كائنين مختلفين في الذاكرة.. وهذا معناه أن فئة النص تتصرف كالسجلات عند إسناد
القيم.
ملحوظة:
يقوم إطار
العمل بمشاركة الذاكرة بين المتغيرات النصية التي تحمل نفس القيمة، وذلك لتوفير
مساحة الذاكرة وتحسين أداء البرنامج، وعند اختلاف قيمة أي متغير، فإنه يخرج من هذا
الاشتراك ويوضع النص الخاص به في ذاكرة مستقلة خاصة به.. لكن هذه التفاصيل لا تعنينا
كثيرا هنا.
أما بالنسبة للسجلات
Structures:
فهي أنواع
قيمية Value Types، وهذا معناه أن
الكائنات المعرفة من السجلات تحمل كامل البيانات المحفوظة في الذاكرة مباشرة،
وليست كالفئات تحمل مجرد عنوان الذاكرة.. وبسب هذا الفارق، لا تحتاج السجلات إلى
استخدام الكلمة New
في تعريفها (وإن كان من الممكن استخدامها في فيجوال بيزيك اختياريا).. فبمجرد
تعريف متغير من السجل، يتم حجز مساحة الذاكرة التي يحتاجها هذا السجل، ويعتبر
السجل موجودا وقابلا للاستخدام في الحال.. أي أن الكود:
Dim I As Integer = 3
مكافئ تماما
للكود:
Dim I As New Integer( )
I = 3
مع ملاحظة أنك
لا تستطيع وضع أي أرقام بين القوسين، لأن السجل Integer ليس له حدث إنشاء (منشئ) Constructor يقبل معاملا Parameter.
ولا يمكن للسجل
أن تكون قيمته منعدمة Nothing،
وحتى لو وضع فيه المبرمج هذه القيمة حصريا، فإنها تؤدي إلى محو خانات السجل
وإعادتها إلى القيم الافتراضية، لكنها لا تؤدي إلى محو السجل نفسه من الذاكرة..
مثال:
Dim I As Integer = 5
I = Nothing
MsgBox(I) ' 0
ونظرا لأن
متغير السجل يحتوي على قيمته الفعلية، فإن إسناد سجل إلى آخر يؤدي إلى نسخ قيم
السجل الأول إلى الثاني، مع بقاء كل منهما مستقلا عن الآخر، وليس كما هو الحال في
نسخ الفئات مرجعيا.
ولهذا الفارق
الجوهري بين الفئات والسجلات، يمكن تعريف متغير داخل الفئة من نفس نوع الفئة:
Class X
Dim A As X
End Class
بينما لا يمكن
تعريف متغير داخل السجل من نفس نوع السجل.. الكود التالي مرفوض:
Structure Y
Dim B As Y
End Structure
ففي الحالة
الأولى ستحتوي الفئة X على مرجع لنسخة أخرى من نفس نوع الفئة X وستكون قيمة المتغير A في البداية Nothing.
بينما في
الحالة الثانية سيحتوي السجل Y على نسخة كاملة جديدة من نفس نوع السجل سيتم حجزها في الذاكرة ووضعها
في المتغير B،
الذي بدوره سيحتوي على نسخة كاملة جديدة من نفس نوع السجل سيتم حجزها في الذاكرة
ووضعها في المتغير B،
وهو بدوره سيحتوي على نسخة كاملة جديدة من نفس نوع السجل سيتم حجزها في
الذاكرة..... وهكذا في دائرة مغلقة، لن تنتهي إلا حينما يتم استهلاك مساحة الذاكرة
بالكامل دون جدوى!.. لهذا لا تسمح لغات البرمجة بهذا الأمر مع السجلات.
لاحظ أن تعريف
متغير داخل الفئة من نفس نوع الفئة يسمح بإنشاء كائنات هرمية، مثل فئة شجرة العرض TreeView Class، التي يخرج من
كل فرع من فروعها فروع أخرى تخرج منها فروع أخرى، وهكذا.
شكراً استاذي علي هذه الإجابة الرائعة
ردحذف