- 0
سجل دخول لمتابعه هذا
متابعين
0

الاضافات في سي#2 - Generics
بواسطة
هاني الأتاسي,
-
يستعرض القسم حالياً 0 members
لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .
بواسطة
هاني الأتاسي,
لا يوجد أعضاء مسجلين يشاهدون هذه الصفحة .
تم النشر منذ (معدل)
Generics
القسم الأول:
تعتبر ال Generics من أشهر الخصائص التي تمت اضافتها في الاصدار الثاني من سي#. وسوف أحاول هنا شرحها بشكل مختصر وسريع.
تمكننا هذه الخاصية من أستخدام أنواع معطيات عامة غير معروفة عند انشائنا للكود، ومن ثم بعد ذلك يمكننا تخصيص هذا الكود ليعمل مع أي معطيات كانت. مثلا ArrayList في الاصدار الأول تقبل اضافة اي كائن من نوع Object ، وبما أن جميع كائنات الدوت نيت تندرج من Object بشكل مباشر أو آخر فإنه يمكن اضافة اي كائن إلى الArrayList حيث يمكن اضافة ارقام ، تواريخ ، اسماء ، إلخ .. طبعا عند استخراجك لهذه الكائنات يجب أن تسند الكائن إلى النوع الصحيح قبل استخدامه ، ولا تنسى أنه عند اضافتك أي قيمة بسيطة struct type فيجب على الruntime ان تقوم بعملية تغليف لهذه القيمة Boxing قبل استخدامها مما ينتج عنه بعض البطئ.
لمحة أولى على كود مكتوب باستخدام Generics :
فوائد استخدام ال Generics :
1- الكفائة في اعادة استخدام الخوارزميات العامة . حيث يقوم مطور ما بكتابة خوارزمية عامة تعمل على أي نوع من أنواع المعطيات ومبرمج آخر يقوم باستخدامها مثلا على نوع معطيات DateTime .
2- مستخدم الخوارزمية يستطيع استخدامها بدون الحصول على الكود المصدري وهذا مختلف عن سي++ templates وجافا.
3- Type Safety: عند استخدام كود ال Generics يكون التعامل مع نوع معطيات واحد وليس مع نوع معطيات عام Object .
4- سرعة أداء البرنامج : عند استخدام أنواع معطيات بسيطة value type ، فلن يحصل اي من ال boxing عند استخدامك للgenerics لأنك تتعامل مباشرة مع النوع البسيط وليس مع Object
ماذا يمكن أن يكون نوع عام Generics:
أي من الأنواع التالية يمكن أن تكون عامة ذات بارامتر واحد أو أكثر: Classes, structs, interfaces, delegates, methods . بعض الأمثلة على استخدام Generics
الأنواع البسيطة Generics في منصة دوت نيت:
كل الcollections في دوت نيت تم اعادة بنائها لتستفيد من الGenerics . تم وضع الأنواع الجديدة تحت System.Collections.Generics وأيضا تم تغير اسمائها ولكنها تؤدي نفس الغرض:
ايضا تم اضافة Interfaces جديدة تدعم ال generics معظم هذه الInterfaces تشابه نفسها الموجودة حاليا:
نلاحظ أن البارامتر للGeneric كلها تبدأ بالحرف T ، هذا الأمر فقط متفق عليه بأنه يفضل بدأ هذا النوع من البارامتر بالحرف الكبير T . هذا الأمر مشابه لبدأ الInterface بالحرف الكبير I
أيضا تم اضافة العديد من التوابع ال static إلى الArray class .حيث يمكنك استخدام هذه التوابع مثلا من أجل ترتيب مصفوفة من أي نوع وهكذا . هذه التوابع هي:
AsReadOnly, BinarySearch, ConvertAll, Exists, Find, FindAll, FindIndex, FindLast, FindLastIndex, ForEach, IndexOf, LastIndexOf, Sort, TrueForAll
طبعا يفضل استخدام هذه التوابع خصوصا على الأنواع البسيطة struct types من أجل سرعتها.
انتاج الكود في الذاكرة للأنواع العامة Generics :
في السي++ عندما نستخدم ال Templates يقوم المترجم بانتاج كود جديد كليا لكل نوع نقوم باستخدامه مع ال Template . هذا قد يجعل حجم الملف التنفيذي كبير إذا أصرفنا في استخدام ال Templates . طبعا هذا الأمر قد تحسن جزئيا في دوت نيت .
آلية تنفيد كود الدوت نيت تقوم بعملية انتاج كود جديد للأنواع Generics في الذاكرة عندما نستخدمها من أجل كل نوع بسيط مختلف. مثلا إذا كان لدينا Stack<int> و Stack<byte> فكل نوع سوف يتم توليده في قسم مختلف في الذاكرة . أما بالنسبة للأنواع من نوع reference فالدوت نيت سوف تقوم بتوليد كود واحد لها في الذاكرة وتستخدم نفس هذا القسم من الذاكرة لأي نوع من نوع reference .
هذه الطريقة منطقية ، فالدوت نيت يمكن أن تشارك الكود إذا كان النوع من نوع reference لأن النوع reference في الذاكرة عبارة عن مؤشر فقط (بالعادة 32 بت). أما الأنواع البسيطة فقد تكون byte أو int (32 بت) و UInt32 أو struct أو غيرها . ولو فكرت بها أكثر لتجد أنه يوجد تعليمات آلة مختلفة من أجل كل نوع بسيط ، فعملية جمع عددين من نوع Int32 تختلف عن عملية جمع عددين من نوع UInt32 .
ويمكن اثبات هذا الموضوع بكتابة برنامج بسيط :
قم بمراقبة قيمة EIP ففي الحالة الأولى والثانية سوف يختلف : وهذا يعني أنا الكود تم انشائه في مكانين منفصلين في الذاكرة ، أما الحالة الثالثة والرابعة فسوف تجد أن EIP نفسها : وهذا يعني أن الكود متشارك عليه في الذاكرة .
القسم الثاني Constrains :
المترجم في حالة ال Generics يتأكد أن الكود المكتوب سوف يعمل في أي نوع من أنواع المعطيات المتاحة ، على سبيل المثال فإن التابع Swap يعمل فلى جميع أنواع المعطيات المتوفرة في ال CLR :
التابع Swap يعمل مع جميع أنواع المعطيات البسيطة struct والreference ، ويمكن استدعاء val1.ToString() لأن جميع الأنوع هي من النوع Object . طبعا لو حاولنا استدعاء التابع val1.Read() لأعطانا المترجم خطأ في الترجمة . لكن ماذا لو من بنائنا للخوارزمية فعلا أردنا فقط استخدام الأنواع من نوع Stream ، وبالتالي يمكننا استدعاء التابع Read .
يمكننا تحديد الأنواع التي يمكن استخدامها في ال Generics بما يسمى Constraints . في هذه الحالة التابع سوف يكون له حرية أكثر في استدعاء التوابع التي يكون مصمم لها . مثال :
في المثال السابق قمنا بتخصيص أن الأنواع التي يمكن استخدامها مع التابع Min يجب أن تكون من النوع IComparable ، وبهذه الحالة يمكن استدعاء arg1.CompareTo .
القسم الثالث Generic Delegates
يمكننا استخدام Generics في تعريف ال Delegates . وهذه الميزة تساعد جدا فقد تسهل علينا في استخدام نفس ال Delegate مع عدة أنواع من المعطيات . وأكبر مثال على هذا هو ال EventHandler delegate . ففي السابق يجب علينا أن نعرف delegate خاص عندما ننشئ event جديد إذا كان يحتاج معلومات غير متوفرة في النوع EventArgs. الآن ليس من الداعي تعريف delegate جديد من أجل كل حالة:
ملخص:
ال Generics تعتبر من اكبر الاضافات في الاصدار الجديد ، وإذا استخدمت بالشكل الصحيح سوف تفيد البرنامج في الأداء وفي اعادة استخدام الكود . ففي السابق كنت أستغرق أكثر من نصف ساعة من أجل كتابة strong typed collection أما مع ال generics فيمكنك أن تصل إلى نفس النتيجة بدون أي وقت يذكر .
بشكل عام يمكنك استخدام الGenerics في هذه الحالات:
1- عند استخدامك لل Collections فهي سريعة جدا فيها
2- الخوارزميات التي يمكن أن نطبق عليها أكثر من نوع من المعطيات كخوارزميات الترتيب والبحث
3- أنواع المعطيات التي يمكن استخدام أكثر من نوع لمحتوياتها
4- العديد من الكود الحالي الذي كتبته ويستخدم Object يمكنك اعادة كتابته للاستفادة من Generics والتخصيص لنوع معطيات معين.
تم تعديل بواسطه هاني الأتاسيشارك هذا الرد
رابط المشاركة
شارك الرد من خلال المواقع ادناه