بسم الله الرحمن الرحيم
Generics
المكتبة BCL(Base Class Library) تحتوي منذ الآن على فضاء اسماء جديد و هو System.Collections.Generic و يحتي هذا الفضاء(اذا صح التعبير) على عدة كلاسات تمكنك من انشاء مجموعات نوعيه (Generic Collections) . و يقال نوعية Generic لأنه عند التعريف أنت تقوم بتحديد مساحة محددة للكائنات التي تريد و لا تقوم بتحديد نوع.
ان ال Generics تمكننا من كسب وقت كبير في عملنا و انت لو كنت قد قمت من قبل بانشاء Collections خاصة بك عن طريق الكود فلن تتجاهل الوقت و الجهد الكبيرين في انشاءها . لكن الآن و مع الفيجوال بيسك 2005 يمكنك عن طريق ال Generics انشاء Collections خاصة بك في سطر واحد لا أكثر.لم يعد ضروريا الآن ان تنشأ Collection لكل نوع فما عليك الآن سوى استعمال هذه الامكانية الجديدة و تحدد النوع الذي تريد انشاء Collection له.
الآن دعونا من الكلام النظري الذي أظن انه غير مفهوم للكثيرين و لننتقل الى سرد أمثلة تطبيقية حتى نتمكن من فهم هذه التقنية الجديدة. لنفرض أن لدينا مثلا Class اسمه Customer يحتوي على خاصيتين (في المثال وضعناهم على شكل متغيرات فقط من أجل الاختصار) :
Public Class Customer Public Name As String Public CreditLimit As Decimal Public Sub New(ByVal CustomerName As String, _ ByVal CustCreditLimit As Decimal) Name = CustomerName CreditLimit = CustCreditLimit End Sub End Class
و الآن عندما أنشأنا كلاس مثل هذا يمكننا انشاء لائحة زبائن بكل سهولة و عن طريق السطر التالي :
Dim Customers As New System.Collections.Generic.List(Of Customer)
استطعنا الآن و بكل سسهولة انشاء لائحة من المستخدمين و التي تحتوي فقط على نوع Customer و تستطيع عن طريق تقنية ال IntelliSense من الوصول الى كائنات ال Customer التي تحتويها هذه اللائحة التي أسميناها Customers. و بامكانك عمل نفس الأمر كانشاء Collections من انواع اخرى كال Product... الآن تستطيع كتابة كود كالتالي :
Dim custA As New Customer("CustA", 1000)
Customers.Add(custA)
Dim custB As New Customer("CustB", 2000)
Customers.Add(custB)
For Each c As Customer In Customers
MessageBox.Show("Customer: " & c.Name & _
", limit: " & c.CreditLimit)
Nextكما تلاحظ ففي السطر الأول قمنا بانشاء زبون جديد و أعطيناه اسم و CreditLimit عن طريق ال Constructor ثم بعد ذلك أضفناه الى لائحة الزبائن التي أنشأناها مسبقا و ذلك عن طريق الmethod المسماة Add و بعد ذلك أنشانا زبون جديد و أضفناه الى اللائحة ثم انتقلنا الى عمل حلقة تكرارية حيث أننا نمر على كل عنصر من عناصر اللائحة Customers و اذا كان العنصر من نوع Customer يعني زبون نقوم باظهار اسمه و رصيده. لكن دعنا نرى الآن الكود أسفله :
'compile error: Product cannot be converted to Customer
Dim prodA As New Product("ProdA", CDec(29.95))
Customers.Add(prodA)كما ترى فقد أنشأنا عنصر جديد لكنه ليس Customer بل Product و أضفناه الى اللائحة Customers لكن هنا سيحدث خطأ لأننا حددنا عندما عرفنا اللائحة انها ستقبل فقط الزبائن Customer فاذا أضفنا عنصر من نوع مخالف سيعطينا الخطأ المكتوب باللون الأخضر.
طبعا ستجد عدة انواع أخرى متفرعة من فضاء الأسماء System.Collections.Generic مثال ذلك Dictionary(of K,V) و LinkedList(of T) و Stack(of T).
يمكنك ان تنشأ Generic خاص بك يتوافق مع احتياجاتك بمساعدة التعريف الجديد Of. لنفرض أن لدينا الكلاس المسمى Comparer و الذي يمكننا من مقارنة عدة عناصر :
Public Class Comparer(Of itemType) '... End Class
يمكنك ايضا ان تضع الزام و تحديد نوع الكلاس الذي يمكن قبوله من طرف هذا الكلاس عند انشاء نسخة جديدة منه و مثال على ذلك :
Public Class Comparer(Of itemType As IComparable) '... End Class
طبعا هنا حددنا و قلنا للكلاس Comparer الا يقبل سوى الكلاسات التي تقوم بعمل Implements لل Interface المسماة IComparable و طبعا يمكنك عمل عدة تحديدات من أجل ان تتفادى أخذاء غير متوقعة.
الآن دعونا نوسع الكلاس Customer الذي رأيناه سابقا و نقوم بعمل Implements للواجهة IComparable
Public Class Customer Implements IComparable Public Name As String Public CreditLimit As Decimal Public Sub New(ByVal CustomerName As String, _ ByVal CustCreditLimit As Decimal) Name = CustomerName CreditLimit = CustCreditLimit End Sub Public Function CompareTo(ByVal obj As Object) As Integer _ Implements IComparable.CompareTo Dim c As Customer = CType(obj, Customer) If CreditLimit > c.CreditLimit Then Return 1 If CreditLimit < c.CreditLimit Then Return -1 Return 0 End Function End Class
كما ترون عملنا Implements للواجهة IComparable و اضيفت لنا ال Function المساة CompareTo و اظن ان الكود جد واضح لكن دعني أشرحه قليلا حتى تكون الامور واضحة جدا. هذه ال Function تقوم بمقارنة العنصر الحالي يعني الكلاس الحالي مع كلاس او عنصر آخر ممرر لها Obj فاذا كان العنصر الحالي أكبر من العنصر الممرر اليها ترجع 1 اما اذا كان أصغر فترجع -1 لكن اذا كانا متساويين فالقيمة المرجعة تكون 0، في السطر الأول من ال Function قمنا بانشاء كائن Customer و أعطيناه قيمة العنصر الممرر الذي نوعه هنا Object و لكي يتلائم مع النوع Cutomer قمنا باستعمال الكلمة المفتاحية CType و بعد ذلك نقوم بالمقارنة.
الآن نقوم بانشاء كلاس Product مثلا و نقوم فيه بمقارنة الأثمنة (Price) و الكود المختصر هو التالي :
Public Class Product Implements IComparable Public Name As String Public Price As Decimal Public Sub New(...)... Public Function CompareTo(...)... End Class
الآن لنرى الكلاس Comparer الذي انشأناه و نرى كيف يقوم بعمل المقارنة بين الأنواع او المقارنة النوعية (generic compare)
Public Class Comparer(Of itemType As IComparable) Public Function GetLargest(ByVal Item1 As itemType, _ ByVal Item2 As itemType) As itemType Dim i As Integer = Item1.CompareTo(Item2) If i > 0 Then Return Item1 If i < 0 Then Return Item2 Return Nothing End Function End Class
كما ترى اضفنا Function جديدة الى الكلاس و أسميناها GetLargest و هي تستقبل عنصرين طبعا من نفس النوع هنا و تقوم بارجاع العنصر الأكبر، في السطر الأول قمنا باعلان متغير i من نوع Integer و يأخذ القيمة المرجعة من التعبير :
Item1.CompareTo(Item2)
طبعا هذا السطر جد واضح فنحن نقوم بمقارنة العنصر Item1 بالعنصر Item2 و نحتفظ بالقيمة المرجعة في المتغير i.
بعد ذلك نقوم بفحص قيمة المتغير i فاذا كان أكبر من 0 اذا فالعنصر Item1 هو العنصر الأكبر فتقوم الدالة بارجاعه اما اذا كانت قيمة المتغير أصغر من 0 اذا فالعكس صحيح و نرجع ال Item2 اما في حالة ما اذا كان المتغير i يساوي 0 اذا فالعنصرين متساويين و عندها لا نرجع شيئا (Return Nothing).
الآن يمكننا العمل بهذا الكلاس بطريقة بسيطة و سهلة و هذا الكود يوضح الكيفية :
Dim pc As New Comparer(Of Product)
Dim prod1 As New Product("LittleOne", 10)
Dim prod2 As New Product("BigOne", 100)
Dim lp As Product = pc.GetLargest(prod1, prod2)
MessageBox.Show("The more expensive product is: " & lp.Name)
Dim cc As New Comparer(Of Customer)
Dim cust1 As New Customer("SmallCo", 1000)
Dim cust2 As New Customer("LargeCo", 5000)
Dim lc As Customer = cc.GetLargest(cust1, cust2)
MessageBox.Show("The customer with a higher limit is: " & lc.Name)كما تلاحظون هنا قمنا باستعمال نفس الكلاس Comparer فمررنا في الأول نوع Product و في الثاني نوع Customer و بدون ال Generics كان يلزمنا ان ننشأ لكل نوع كلاس خاص مثلا ProductComparer و CustomerComprarer.
باستعمال ال Generics تستطيع كسب الوقت و الجهد في عمل أشياء يمكنك الآن تجاوزها و عملها في أقل وقت ممكن، لا تتردد في استعمال ال Generics مع الكلاسات التي تتغير فقط من حيث طبيعة النوع الخاص بها.
اتمنى ان يكون هذا المقال قد نال رضاكم و يمكنكم قراءة المزيد من المقالات في هذا المجال عبر زيارتكم هذا الموقع
و السلام عليكم