مستويات مبتدئ و متوسط -ج2

212
م الدين الرز حسا إعداد: المهندس- 0 -

Upload: alaa-kreem

Post on 07-Aug-2015

16 views

Category:

Software


0 download

TRANSCRIPT

Page 1: مستويات مبتدئ و متوسط -ج2

- 0 - إعداد: المهندس حسام الدين الرز

Page 2: مستويات مبتدئ و متوسط -ج2

- 1 - إعداد: المهندس حسام الدين الرز

الجزء الثاني

المستوى:

.مبتدئ

.متوسط

اعداد:المهندس حسام الدين الرز

Page 3: مستويات مبتدئ و متوسط -ج2

- 2 - إعداد: المهندس حسام الدين الرز

بسم اهلل الرمحن الرحيم

ت

أن

ك

اا إن

نتم

الا ع

ا إلا م

ن ل

م

ل عل

ك

ان

ح

بس

كيم

ح

ال

ليم

ع

ال

Page 4: مستويات مبتدئ و متوسط -ج2

- 3 - إعداد: المهندس حسام الدين الرز

اهداء:

ن اسمي عطر ي اب معطرا ب ا الكت دم هد .. اق ي ق دمش

ا.. ة سوري الب ة الغ ب ي حب لي إل إ

.. ب ري رج الف ل والف لاص الغاج الخ ي ي عب اء س ت ب وا دعوا الله لها و لأ

كرها..ر عن س ي ا عج

مي الب لي ا إ

كرا.. اة مت ة إلحت لب ي مشو ي حملب

لي والدي الد إ

.. ي وات ي وا خ

وت خ لي ا إ

ي ق ف الدمش ي السي

ق كر صدب الد هم ي من ص

ي و ا خ لب ن علي ق الي ي الغ

ات لي ا صدق ..إ

.. ة الب ي الغ ب وج ها ر اعن اة و مت ي هموم إلحت

اطرت ش لي من ت إ

ن الرر جشام الدي

Page 5: مستويات مبتدئ و متوسط -ج2

- 4 - إعداد: المهندس حسام الدين الرز

في حال وجود أي استفسار يرجى التواصل على اإليميل:

[email protected]

Page 6: مستويات مبتدئ و متوسط -ج2

- 5 - إعداد: المهندس حسام الدين الرز

تعتبر لغة السي شارب الموضوع الساخن الذي يطرحه المبرمجون اليوم أثناء حديثهم عن تطوير التطبيق ات وتعد هذه اللغة ثورة نوعية في األوساط البرمجية شبيه بتلك الثورة التي حدثت في

لت فهي مازا 0222صدرت هذه اللغة في حزيران عام التسعينات عندما صدرت لغة برمجة الجاف ا.بقيادة أندرس Microsoftبواسطة فريق Microsoftحديثة العهد. تم أنشاءها من قبل شركة

لك بما في ذ ق ام بإنتاج منتجات ولغات برمجة أخرى Microsoftهيجلزبرج وهو مهندس متميز في وبورالند دلفي" وركز المهندس في السي شارب على أخذ نق اط القوة التي ++C"بورالند توربو

فيها اللغات األخرى مع أضافة التحسينات لجعل هذه اللغة أفضل. تتصف

لتعمل على منصة خاصة بها تسمى تلك المنصة Microsoftمن قبل شركة #Cلقد صممت لغة ال #Cدون االعتماد المباشر على نظام التشغيل ف الشيفرة المكتوبة بلغة – NET.بإطار عمل

NET.ا مع إطار عمل تتخاطب مع نظام التشغيل مباشرة وإنم

مجموعة من العمليات واالجراءات ضمن مكتبة ضخمة جدا توفر Microsoftلقد صممت شركة هذه المكتبة على المبرمجين عناء كتابة الكثير من الشيفرات البرمجية التي يمكن أن توجد بصورة

هذا واضح من و NET.مجردة أو بشكل قياسي لالستخدام العام. تسمى هذه المكتبة بإطار عمل هي اللغة االم لكتابة تطبيق ات #Cوالتي تشير إلى أن لغة Microsoftخالل تصريحات شركة

.NET.تعتمد على منصة

( Object-oriented programming -OOPهي لغة كائنية التوجه ) #Cإن لغة برمجة ولن أبالغ Visual Basicوبين سهولة وبساطة البرمجة بلغة ++Cتجمع بين القوة البرمجية للغة

مقدمة:

Introduction:

Page 7: مستويات مبتدئ و متوسط -ج2

- 6 - إعداد: المهندس حسام الدين الرز

وابتعدت Javaو Delphiإذا ق لت أن هذه اللغة ق امت بجمع مزايا لغات البرمجة السابقة مثل عن مساوئ هذه اللغات و أخطائها.

:#C األهداف من أنشاء لغة برمجة. ومن أهداف لغةجملة من Microsoftتضع شركة

Javaاصة باللغات مثل لتقضي على التعقيدات والمشاكل الخ #Cجاءت :لغة بسيطة -1والقوالب والتوارث المتعدد فهذه تسبب االلتباس لدى غاء الماكروالفق امت ب ++Cو

أول مرة ف ال داعي #Cوكذلك ظهور المشاكل. إذا كنت ممن يدرسون ++Cمطوري .لدراسة هذه الموضوعات

األوامر هي منلغة حديثة: أن معالجة االستثناء وأنواع البيانات الق ابلة للتوسع وكذلك أ -0وهذا ++Cو Cمكون أساسي في لغتي pointer سمات تتصف بها اللغات الحديثة

المكون من أكثر األجزاء التي تسبب االلتباس لدى المبرمجين. وقد تم ألغاء العديد من . ال تق لق بشأن المكونات سوف #Cالتعقيدات والمشاكل التي يحدثها هذا المكون في

.الق ادمة يتم شرحها في الدروسصف لكي تكون لغة البرمجة كائنية البد لها من مف اهيم أساسية تتلغة برمجة كائنية التوجه: -3

وتعدد األوجه Inheritance والتوارثcapsulation بها وهي الكبسلةPolymorphism تدعم لغة السي شارب كل هذه المف اهيم وسنتعرف على كل هذه

.المف اهيم في الدروس المتقدمةوية ومرنة: ق لنا سابق ا ال حدود لهذه اللغة فقط أطلق العنان لخيالك فيمكننا استخدام لغة ق -4

لغة السي شارب في المشاريع الكبيرة ذات األشكال المتعددة كالبرامج الرسومية وجداول للغات أخرى. compilers البيانات وبرامج

ق ليلة وهي األساس التي كلمات دليليه أو أساسية #Cلغة ذات كلمات ق ليلة: تستخدم لغة -5تبنى عليه إ جراءات اللغة. قد تعتقد أن اللغة ذات العديد من الكلمات األساسية هي لغة

Page 8: مستويات مبتدئ و متوسط -ج2

- 7 - إعداد: المهندس حسام الدين الرز

ستجد أنها لغة يمكن #C ولكن هذا غير صحيح فعندما تقوم بالبرمجة باستخدام لغة قوية .استخدامها في أداء أي مهمة. سنتعرف على الكلمات األساسية الحق ا

أي أصناف وتحتوي على أساليب Classesتكتب على شكل #Cاألوامر في لغة نمطية: -6 العضو وهذه األصناف يمكن إعادة استخدامها في برامج أخرى.

ستتمكن من تصميم أعقد التطبيق ات وبمجهود أق ل بكثير #Cويكفي أن نقول إنك بواسطة لغة من الذي يمكن أن تبذله باستخدام لغات برمجة أخرى.

كتاب؟لمن هذا ال

يستهدف هذا الكتاب األشخاص الذين يودون تعلم البرمجة ويودون البدء بلغة برمجية جديدة أما #Cويستهدف أيضا المبرمجين المبتدئين الذين يرغبون تطوير التطبيق ات بواسطة لغة

ف إن هذا الكتاب Visual Basicبالنسبة لألشخاص الذين سبق وأن تعلموا لغة برمجية سهلة مثل .#Cوسيلتهم الحتراف لغة هو

سواء العربية #Cوكخالصة: ف إن هذا الكتاب موجه إلى كل شخص سأم من الكتب التي تتناول لغة أو حتى األجنبية والتي تفترض منه معرفة مسبقة بلغة برمجية أخرى.

إن هذا الكتاب مثالي لنوعين من المبتدئين:

إذا كنت مبتدئا في عالم البرمجة واخترت لغةC# لتبدأ معها مشوارك سيساعدك هذاالكتاب على تعلم مف اهيم برمجية قوية جدا ستعتبر ركيزة أساسية تعتمد عليها أثناء تصميم

تطبيق اتك. إذا كانت لديك خبرة مسبقة بإحدى لغات البرمجة ولو كانت خبرة ضئيلة ولكنك تود تعلم

مثل ثورة برمجية بحد ذاته ي NET.ف إطار عمل NET.كيفية البرمجة باستخدام إطار عمل ويدخل عددا من المف اهيم الجيدة على عالم البرمجة هذا ويقدم الكتاب المف اهيم البرمجية

وإذا كنت مبرمجا بإحدى لغات البرمجة التي ال NET.كائنية التوجه المتعلقة بإطار عمل

Page 9: مستويات مبتدئ و متوسط -ج2

- 8 - إعداد: المهندس حسام الدين الرز

ن يعلمك مف اهيم لتدعم البرمجة كائنية التوجه ف إننا قد أفردنا جزءا كامال في هذا الكتاب وحسب وإنما إتق ان هذه المف اهيم أيضا. NET.البرمجة كائنية التوجه في

ما الذي تحتاج إليه الستخدام هذا الكتاب؟

األداة التي Compilerيمثل المترجم #Cإن أهم ما تحتاج إليه الستخدام هذا الكتاب هو مترجم ويأتي هذا المترجم كجزء من مجموعة إلى برنامج تنفيذي #Cستحول شيفرتك المكتوبة بلغة

( والتي يمكنك جلبها من موقع شركة NET Framework SDK.) NET.تطوير إطار عمل Microsoft

ولكن كي تتمكن من االستف ادة القصوى من هذا الكتاب ف أنت بحاجة إلى بيئة التطوير المتكاملة Visual Studio.NET 2013 والتي تبسط عليك كتابة شيفرةC# من عدة جوانب كما أنها

وذلك ألنها تحوي على مصمم مرئي Windowsمفيدة جدا وربما أساسية لتطوير تطبيق ات للنماذج يحول تصميم واجهات المستخدم إلى متعة حقيقة.

:Visual Studio.NET 2013تحميل نسختك من

وهي: Visual Studio.NET 2013ثالث نسخ من Microsoftأصدرت شركة

1- Visual Studio Express 2013 .وهي نسخة مجانية

Page 10: مستويات مبتدئ و متوسط -ج2

- 9 - إعداد: المهندس حسام الدين الرز

0- Visual Studio Professional 2013 .وهي نسخة مدفوعة الثمن

3- Visual Studio Ultimate 2013 .وهي نسخة مدفوعة الثمن

Page 11: مستويات مبتدئ و متوسط -ج2

- 10 - إعداد: المهندس حسام الدين الرز

اجلزء الثاني الربجمة كائنية التوجه“OOP”

Page 12: مستويات مبتدئ و متوسط -ج2

- 11 - إعداد: المهندس حسام الدين الرز

الفصل األول

مدخل إىل الربجمة كائنية التوجه

وصيغها ويمكننا بعد قراءة الجزء األول كتابة #Cلجزء األول أساسيات البرمجة في لقد تناولنا في ا

وإطار عمل #Cمفيدة لكن كي نتمكن من الوصول إلى القوة الحقيقية للبرمجة بلغة Consoleتطبيقات

.NET فإن علينا أن نستخدم تقنيات البرمجة كائنية التوجهObject-Oriented Programming أو

وفي الحقيقة لقد استخدمنا هذه التقنيات مسبقا في أمثلتنا إال أننا لم نشر إلى ذلك أثناء OOPرا اختصا

شرحنا لألمثلة بهدف تبسيط األمور في البداية.

سوف نتجنب في هذا الفصل الشيفرات البرمجية وسنركز على مبادئ البرمجة كائنية التوجه وهو ما

عد ذلك باعتبار أن عالقتها بالبرمجة كائنية التوجه وثيقة جدا سوف ب #Cسيقودنا إلى كتابة شيفرة بلغة

مبادئ التي سنتناولها في هذا الفصل بتفصيل أكبر في الفصول القادمة ومع الكثير من النتكلم عن جميع

التطبيقات واألمثلة لذا ال داعي ألن تقلق إذا لم تستطع استيعاب كافة األمور في هذا الفصل من الوهلة

ولى.األ

ننا سنتحدث عن أساسيات البرمجة كائنية التوجه ويتضمن ذلك اإلجابة على أسئلة جوهرية مثل إوكبداية ف

"ما هو الكائن؟" سوف تكتشف وجود العديد من المصطلحات المرتبطة بالبرمجة كائنية التوجه التي قد

ستجد أيضا أن استخدام .لهاتظهر مربكة نوعا ما في البداية إال أنك ستجد الكثير من الشرح الوافي

البرمجة كائنية التوجه يتطلب منا رؤية مختلفة للبرمجة.

وباإلضافة إلى مناقشة المبادئ األساسية للبرمجة كائنية التوجه فإننا سنتناول إحدى المناطق التي يكون

ؤية يوفر هذا النوع من التطبيقات ر Windows Formsأمرا جوهريا وهي تطبيقات OOPفيها فهم

ضمن بيئة تطبيقات OOPواضحة للبرمجة كائنية التوجه حيث سنستعرض النقاط األساسية في

Windows Forms.

توضيح:

تلك التطبيقات التي تستفيد من المزايا التي يقدمها نظام Windows Formsنقصد بتطبيقات

Windows .الخ..وبيئته الرسومية من قوائم وأشرطة أدوات.

Page 13: مستويات مبتدئ و متوسط -ج2

- 12 - إعداد: المهندس حسام الدين الرز

مالحظة:

حقيقية وهذا يعني أن بعضا من التقنيات OOP.NETالمقدمة في هذا الفصل هي OOPن الحظ أ

ن التنويه عن ذلك سيبسط علينا األمور إاألخرى OOPالمعروضة في هذا الفصل ال تنطبق على بيئات

الحقا في هذا الفصل خصوصا إذا كنت تبرمج بلغة برمجة أخرى.

ما هي البرمجة كائنية التوجه؟

What is Object Oriented Programming?

أسلوبا جديدا في إنشاء تطبيقات الحاسب والذي يتجاوز العديد من المشاكل تعتبر البرمجة كائنية التوجه

التي قد نواجهها باستخدام أسلوب البرمجة التقليدية إن نوع البرمجة الذي تعرفنا عليه حتى اآلن يسمى

والذي ينتج عنه تطبيقات موحدة وهذا يعني أن جميع procedural programmingبالبرمجة اإلجرائية

وظائف التطبيق موجودة ضمن عدد من الوحدات البرمجية وفي الغالب لن يكون هناك سوى وحدة برمجية

يمكننا استخدام العديد من وحدات الشيفرة OOPفإن الوضع مختلف فباستخدام OOPواحدة أما في

صة وبحيث تكون هذه الوحدة مستقلة بصورة كاملة أو جزئية عن الوحدات بحيث تقدم وحدة وظائفها الخا

األخرى يوفر لنا هذا النهج المعياري للبرمجة جوانب برمجية عديدة باإلضافة إلى توفير الفرصة إلعادة

استخدام الشيفرة في أكثر من تطبيق.

رة السباق إن تطبيقنا الذي ولكي نصف ذلك بصورة أدق تخيل تطبيقا ما ذا أداء عال في جهازك كسيا

استخدمنا فيه تقنيات البرمجة التقليدية مشابه لسيارة السباق المكونة من وحدة واحدة فإذا أردنا أن نحسن

راء إلى المصنع لكي يتمكن خب بإرسالهامن هذه السيارة فإن علينا إما استبدالها بسيارة أخرى جديدة وذلك

في هذا OOPأن نشتري واحدة جديدة أما إذا كنا قد استخدمنا تقنيات تحديثها وإما من ميكانيك السيارات

التطبيق فإن ذلك مشابه لمجرد شراء محرك جديد من المصنع وإتباع تعليمات استبدال المحرك القديم بهذا

الجديد.

من ثم و يكون سير التنفيذ في التطبيقات التقليدية عادة بسيطا وخطيا حيث يتم تعديل التطبيق إلى الذاكرة

ومن ثم إزالة التطبيق من الذاكرة وخالل هذا الطريق Bوإنهاء التنفيذ عند النقطة Aبدء التنفيذ من النقطة

هناك العديد من األشياء المستخدمة مثل الملفات ووسائط التخزين أو إمكانية بطاقة العرض لكن الجسم

جد العديد من عمليات معالجة البيانات وفقا الرئيسي للمعالجة يتم في مكان واحد وخالل هذه الشيفرة سن

لمبادئ رياضية ومنطقية وطرق معالجة البيانات تكون بسيطة وتستخدم أنواع بيانات بسيطة أيضا مثل

القيم العددية والمنطقية لبناء تمثيالت للبيانات أكثر تعقيدا.

تحقق النتائج نفسها OOPبصورة خطية فعلى الرغم من أن تقنية OOPإن األمور نادرا ما تسير في

معدة وفقا لبنية البيانات ومعانيها باإلضافة إلى التفاعل OOPالتي تحققها التقنيات التقليدية إال أن تقنيات

ي تصميم التطبيقات يتطلب جهدا أكبر ف OOPبين البيانات والبيانات األخرى يبدو ذلك أن استخدام تقنيات

ة مزايا كمجاالت التوسع والتطوير المستقبلية دنستفيد عندها من عفي الحقيقة إن هذا صحيح إال أننا س

Page 14: مستويات مبتدئ و متوسط -ج2

- 13 - إعداد: المهندس حسام الدين الرز

فمتى استطعنا إنشاء نوع وأسلوب معين الستخدامه في تطبيق ما فإن بإمكاننا استخدام نفس النوع

واألسلوب في أي تطبيق أخر.

إذا ما هو الكائن؟ Objectبالكائن OOPيسمى هذا النوع في تقنية

ما هو الكائن؟

What is Object?

حيث يمكن تغطي هذه الكتلة جزء من التطبيق OOPعبارة عن كتلة برمجية لتطبيق Objectالكائن

لهذا الجزء أن يعالج كمية من البيانات سواء كانت هذه البيانات محددة أو مجردة.

من تتض التي سبق وتحدثنا عنها في الفصول السابقة والتي structيمثل الكائن في أبسط صوره البنية

فظ البيانات التي يحت .أعضاء من متحوالت وتوابع يمكن ان تمثل المتحوالت الموجودة ضمن هذه البنية

دا هناك كائنات أكثر تعقي .وأما التوابع فهي تعطينا إمكانية الوصول إلى وظائف هذا الكائن .بها الكائن

تخدم على سبيل المثال يمكننا ان نسحيث ال تتضمن اية بيانات وإنما يمكنها أن تحتوي على توابع فقط

كائنا يمثل تخاطب التطبيق مع الطابعة وبالتالي يمكن لهذا الكائن أن يحتوي على توابع تتحكم بصورة

كاملة في الطابعة كطباعة مستند ما أو طباعة ورقة تجريبية...الخ.

OOPيطلق على نوع الكائن في من األنواع تماما كما نقوم بإنشاء المتحوالت #Cيتم إنشاء الكائنات في

يمكننا أن نستخدم تعريفات الصنف لتهيئة الكائنات وهذا يعني إنشاء حالة class )الفئة( بالصنف

instance .محددة وحقيقية من هذا الصنف

مالحظة:

الحظ ان تعبير كائن وحالة من الصنف يمثالن الشيء ذاته لكن الحظ ان تعبير كائن وصنف هما شيئان

ان فالكائن يتبع صنفا محددا تماما كما تتبع المتحوالت ألنواعها.مختلف

Universal Modeling (UML)سوف نصور األصناف والكائنات في هذا الفصل باستخدام صيغة

Language فلغةUML هي لغة مصممة لتشكيل التطبيقات من الكائنات التي تكونها ووصوال للعمليات

ت المتوقعة لهذه الكائنات سوف نستخدم هنا أساسيات هذه اللغة وسنركز على التي تؤديها واستخدام الحاال

OOP .دون الخوض في تفاصيل أكثر تعقيدا

مالحظة:

.Microsoft Visioلقد تم تصميم األشكال في هذا الفصل بواسطة

:Printerلصنف الطابعة وهو باسم UMLتمثيال بلغة (1-1الشكل )يمثل

Page 15: مستويات مبتدئ و متوسط -ج2

- 14 - إعداد: المهندس حسام الدين الرز

(1-1الشكل )

ظ أن اسم الصنف ظاهر في القسم العلوي من المخطط سنهتم باألقسام السفلى الحقا.نالح

:myPrinterحيث لهذه الحالة االسم Printerالتالي يستعرض حالة للصنف UMLوتمثيل

(1-2الشكل )

نالحظ هنا أن اسم الحالة ظاهر في القسم العلوي من المخطط متبوعا باسم الصنف حيث تم فصل كال

سمين باستخدام الرمز ":".اال

Page 16: مستويات مبتدئ و متوسط -ج2

- 15 - إعداد: المهندس حسام الدين الرز

الخصائص والحقول:

Properties and Fields:

هي ما نالكائالوصول على البيانات الموجودة ضمن الكائن إن بيانات والحقول إمكانيةتوفر الخصائص

يميز كل كائن عن اآلخر أي أنه يمكن أن يكون لدينا عدة كائنات من صنف واحد بحيث تختلف هذه

بعضها بالقيم المخزنة ضمن خصائصها وحقولها. الكائنات عن

ذلك الكائن. stateإن مختلف أجزاء البيانات الموجودة ضمن الكائن تمثل حالة

لنفترض أننا نود تمثيل فنجان من القهوة باستخدام مفاهيم البرمجة كائنية التوجه سنحتاج في هذه الحالة

ي الكائن لصنف يعرف جميع كائنات فناجين القهوة سنسم على كائن ليمثل فنجان القهوة ويجب ان يتبع هذا

وعندما نقوم بإنشاء كائن من هذا الصنف علينا أن نزود هذا الكائن CupOfCoffeeهذا الصنف باسم

بحالة معينة لكي يكون لهذا الكائن مدلول ومعنى سنستخدم هنا الخصائص والحقول الجديدة لتحديد نوع

ء كانت قهوة بحليب أو بسكر أو أنها نسكافيه ...الخ عندئذ سيكون لكائن فنجان القهوة المستخدمة مثال سوا

القهوة حالة معينة مثال فنجان قهوة بن برازيلي بالحليب وبقطعتين من السكر تمثل الخصائص والحقول

وع أو قيما من ن Stringأماكن تخزينية لذا يمكننا ان نحفظ ضمنها معلومات كالمتحوالت النصية من نوع

int خر من أنواع البيانات ويمكن أن تمثل هذه المعلومات كائنات تابعة ألصناف أخرى ولكن نوع آأو أي

تختلف الخصائص عن الحقول في طريقة قراءة وإسناد المعلومات إليها فالحقول كالمتحوالت تمكننا من

يام بأمور معينة تمكننا من الق الوصول إلى قيمها وتعديلها بصورة مباشرة أما الخصائص فهي توفر لنا ألية

CupOfCoffeeمحددة عند قراءة أو إسناد القيم إليها فإذا استخدمنا حقال لحفظ عدد قطع السكر في كائن

فإن المستخدمين يمكن أن يضعوا أي قيمة يرغبون بها ضمن هذا الحقل وذلك بحسب نوع الحقل طبعا أما

2و 0مجال محدد لقيمة هذه الخاصية لنفرض مثال قيمته ما بين إذا استخدمنا خاصية فإن بإمكاننا التقيد ب

.فقط

وبصورة عامة يفضل استخدام الخصائص بدال من الحقول عند الوصول إلى حالة الكائن باعتبار ان لدينا

أي أن read-onlyتحكما اكبر بالقيم المخزنة ضمنها يمكن لخصائص معينة ان تكون قابلة للقراءة فقط

راءة محتوياتها لكن ال يمكننا تعديل قيمتها على األقل ليس بصورة مباشرة ويعد ذلك تقنية مفيدة بإمكاننا ق

إذا أردنا قراءة أجزاء عديدة من حالة الكائن في آن واحد قد يكون لدينا خاصية للقراءة فقط مفيدة إذا أردنا

للقراءة فقط ضمن الصنف ن واحد قد يكون لدينا خاصية أجزاء عديدة من حالة الكائن في آ قراءة

CupOfCoffee تسمى بـDescription حيث تعيد هذه الخاصية سلسلة نصية تمثل حالة الكائن في

هذا الصنف كوصف لفنجان القهوة وذلك عند الطلب يمكننا ان نحصل على النتيجة نفسها بتجميع البيانات

والجهد والشيفرة الزائدة ويمكن أن الموجودة في عدة خصائص إال أن هذه الخاصية توفر علينا الوقت

وهي تعمل بصورة مشابهة لخصائص القراءة فقط. write-onlyتكون هناك خصائص للكتابة فقط

وباإلضافة إلى ميزة القراءة/الكتابة في الوصول إلى الخصائص فإنه يمكننا ان نحدد أسلوبا مختلفا للوصول

ماهي أجزاء الكائن التي يمكننا الوصول إليها في إلى الحقول والخصائص أيضا تحدد هذه الوصولية

Page 17: مستويات مبتدئ و متوسط -ج2

- 16 - إعداد: المهندس حسام الدين الرز

شيفرتنا البرمجية أي إما أن تكون هذه األجزاء متوفرة لكل الشيفرة البرمجية أي أنها عامة أو أن تكون

privateهذه األجزاء مقتصرة على شيفرة الصنف فقط أي أنها خاصة يمكننا مثال إنشاء حقول خاصة

مثال هذا يعني أن الشيفرة الموجودة ضمن publicل خصائص عامة وتوفير الوصول إليها من خال

الصنف يمكن أن تصل إلى هذه الحقول بصورة مباشرة أما الخاصية العامة فإنها وسيلة التخاطب مع

الشيفرة خارج الصنف والواقي من تعديل هذه البيانات بصورة خاطئة تعرف األعضاء العامة في الصنف

فة منه.بأنها األعضاء المكشو

إن ذلك مشابه لمبدأ مدى المتحوالت فالحقول والخصائص الخاصة هي أعضاء محلية بالنسبة للكائن بينما

مدى الحقول والخصائص العامة يشتمل على الشيفرة خارج الكائن.

للصنف لعرض الخصائص والحقول وذلك كما يلي: UMLسنستخدم القسم الثاني من تمثيل

(1-3الشكل )

حيث يتضمن على خمسة أعضاء خصائص أو حقول حيث لم CupOfCoffeeتمثيل الصنف ذلك يمثل

( معرفة كما شرحنا ذلك مسبقا ولكل عضو من هذه األعضاء المعلومات التالية:1-3) نحدد ذلك في الشكل

:لألعضاء الخاصة وبصورة فيستخدم -الرمزيستخدم الرمز+ لألعضاء العامة أما الوصولية

ن أعرض األعضاء الخاصة ضمن األشكال في هذا الفصل وذلك باعتبار أن هذه عامة فإنني ل

المعلومات هي معلومات داخلية للصنف.

.اسم العضو

.نوع العضو

وقد استخدم الرمز ":"لفصل اسم العضو عن النوع.

Page 18: مستويات مبتدئ و متوسط -ج2

- 17 - إعداد: المهندس حسام الدين الرز

المناهج:

Methods:

ة التي دعيها بنفس الصورإلى التوابع التي يحتويها الصنف يمكننا أن نست methodيشير مصطلح المنهج

نستدعي فيها أي تابع آخر ويمكننا ان نستخدم القيم المعادة والبارامترات بنفس الصورة أيضا عد إلى

لمزيد من التفاصيل حول ذلك. من الجزء األول الفصل السادس

مالحظة:

ارة عن مناهج عب ()Mainإن جميع التوابع التي أنشأناها في الفصول السابقة باإلضافة إلى التابع

وتتبع هذه المناهج إلى الصنف الذي يمثل التطبيق.

تستخدم المناهج للوصول إلى وظائف الكائنات وكما في الخصائص والحقول فإن المناهج أيضا يمكن ان

تكون عامة او خاصة بحيث يقتصر استخدامها ضمن الكائن فقط أي ال يمكننا استدعائها من خارج شيفرة

ن تستخدم من قبل شيفرة خارجية وفي أغلب األحيان تعتمد هذه المناهج على حالة الصنف أو يمكن أ

ومن الجدير بالذكر التنويه إلى أن لهذه المناهج إمكانية الوصول إلى (وحقوله خصائصهقيم )الكائن

ان يعرف منهجا باسم CupOfCoffeeاألعضاء الخاصة ضمن الكائن على سبيل المثال يمكن لصنفنا

AddSugar() (ان كان هذا الحقل خاصعدد قطع السكر بمقدار معين )حتى إ بحيث يقوم بزيادة قيمة حقل

يمكننا ان نعرض المناهج بالصورة التالية: UMLوباستخدام تمثيل

(1-4الشكل )

Page 19: مستويات مبتدئ و متوسط -ج2

- 18 - إعداد: المهندس حسام الدين الرز

عدا أن النوع الذي يظهر في النهاية يمثل والخصائص فيماإن الصيغة هنا مشابهة لنفس صيغة الحقول

.البارامتراتقيمة المعادة باإلضافة إلى وجود الئحة من نوع ال

مالحظة:

وتستخدم inoutأو outأو inبواسطة واحد من المحددات التالية: UMLيتم عرض كل بارامتر في

#Cيوافقان كلمتي outو inoutهذه المحددات لإلشارة إلى اتجاه سير البيانات حيث أن المحددين

عندما ال نضع أي #Cفتوافق سلوك لغة inعلى الترتيب أما الكلمة outو refالمقابلتين لهما وهما

من هاتين الكلمتين.

كل شيء عبارة عن كائن:

Everything's an Object:

NET.وإطار عمل #Cفي الحقيقة إن كل شيء في لغة أكثرن كي نوضح لك األمور حان الوقت اآللقد

يمثل منهجا لصنف وكل نوع Consoleالموجود ضمن تطبيق () Mainهو عبارة عن كائن فالتابع

متحول رأيناه هو عبارة عن صنف إن كل أمر استخدمناه كان يمثل إما خاصية أو منهجا مثل الخاصية

<String>. Length والمنهج<String>.ToUpper() . الخ إن النقطة "."في االمر هنا تفصل اسم..

.الكائن عن اسم الخاصية أو المنهج

طة للغاية في أغلب األحيان وصيغة استخدامها بسي #Cستجد الكائنات في كل مكان أثناء البرمجة بلغة

ن وصاعدا سنركز على الكائنات بتفصيل أكبر ليبقى في بالك ان المفاهيم التي سنتحدث عنها هنا من اآل

الذي intمثل النوع #Cي ومهما كانت صعوبة فهمها بالنسبة إليك إال أنها مطبقة على أبسط العناصر ف

استخدمناه بكثرة ولحسن الحظ يمكننا ان نستخدم هذه النقطة لصالحنا في محاولة لفهم هذه المفاهيم

األساسية.

دورة حياة الكائن:

The Lifecycle of an Object:

ة قيد للكل كائن دورة حياة تبدأ بالتصريح عن الكائن وتنتهي بتدمير هذا الكائن وباإلضافة إلى مرح

االستخدام فإن هناك مرحلتان رئيسيتان في دورة حياة الكائن:

(: وهي المرحلة التي يتم تهيئة الكائن فيها وتسمى هذه التهيئة constructionمرحلة البناء )

بالبناء وتتم ضمن منهج بناء الكائن.

م ببعض المهام (: فعند عدم حاجتنا الستخدام الكائن فإننا سنقوdestructionمرحلة التدمير )

كتحرير جزء من الذاكرة مثال وتلك مهمة منهج التدمير.

Page 20: مستويات مبتدئ و متوسط -ج2

- 19 - إعداد: المهندس حسام الدين الرز

مناهج البناء:

Constructors:

تتم تهيئة الكائن بصورة تلقائية فنحن لن نخوض في تفاصيل وضع كائن جديد ضمن الذاكرة مثال إال أننا

يما ل المثال يمكننا أن نضع ققد نحتاج عادة إلى القيام بمهام إضافية خالل مرحلة تهيئة الكائن على سبي

افتراضية ضمن خصائص هذا الكائن أثناء بناءه.

( وهو منهج ال يتضمن أية بارامترات ويأخذ default constructorلجميع الكائنات تابع بناء افتراضي )

نفس اسم الصنف ويمكن أن يتضمن تعريف الصنف العديد من مناهج البناء ويمكن أن يكون لهذه المناهج

واقيع مختلفة يمكن استخدامها بواسطة الشيفرة الخارجية لتهيئة وإنشاء كائن من هذا الصنف.ت

تستخدم المناهج التي تتطلب بارامترات عادة لتزويد الكائن بقيم افتراضية للبيانات التي ستخزن ضمنه.

وع ننشئ كائنا من ن المثال يمكننا أنعلى سبيل newباستخدام الكلمة #Cيتم استدعاء مناهج البناء في

String :باستخدام األسلوب التالي

();string newmyString = string

توضيح:

( والسبب String( وأما اآلن فإننا نقول )كائن من نوع Stringفي السابق كنا نقول )متحول من نوع

في ذلك أن األنواع عبارة عن أصناف.

اضي فإن البناء االفترهج بناء غير المنهج االفتراضي وكما في منهج ويمكننا إنشاء الكائنات باستخدام من

مناهج البناء غير االفتراضية لها نفس اسم الصنف إال انها تتضمن بارامترات وتستخدم بنفس صيغة

استخدام منهج البناء االفتراضي كما يلي:

,10);'a'(string newmyString = string

".aaaaaaaaaaذا بإنشاء سلسلة نصية جديدة تأخذ القيمة "سيقوم منهج البناء ه

ويمكن لمناهج البناء كما في باقي مناهج الكائن األخرى أن تكون عامة أو خاصة فال يمكن لشيفرة خارجية

أن تقوم بإنشاء كائن باستخدام منهج بناء خاص وإنما يجب أن تستخدم منهج بناء عام وبهذه الطريقة فإننا

مي أصنافنا على استخدام منهج بناء غير المنهج االفتراضي.نخبر مستخد

قد ال يكون لبعض األصناف مناهج بناء عامة مما يعني انه من المستحيل لشيفرة خارجية أن تقوم بإنشاء

حالة من هذه األصناف لكن الحظ ان ذلك ال يعني ان تكون هذه األصناف عديمة النفع كما سنرى الحقا.

Page 21: مستويات مبتدئ و متوسط -ج2

- 20 - إعداد: المهندس حسام الدين الرز

مناهج التدمير:

Destructors:

لعمليات التنظيف بعد إنهاء العمل بالكائن وبصورة عامة فإننا ال NET.تستخدم مناهج التدمير من قبل

نقدم شيفرة لمنهج التدمير وإنما يستخدم منهج افتراضي لذلك ولكن يمكن أن نستخدم تعليمات محددة ألي

شيء مهم نحتاج للقيام به قبل حذف الكائن.

مالحظة:

هم ان تتعلم أن منهج تدمير الكائن ال يتم استدعاؤه عندما نتوقف عن استخدام الكائن.من الم

عندما يخرج المتحول عن المدى فإننا لن نتمكن من الوصول إليه في الشيفرة الحالية إال أنه ال يزال

NET. في CLRموجودا في مكان ما من ذاكرة الحاسوب ولن تتم إزالته نهائيا إال عندما يقوم نظام

بتجميع نفايات تطبيقنا من الذاكرة عند تدمير الكائن نهائيا.

مالحظة:

هذا يعني أن علينا أال نعتمد على منهج التدمير لتحرير المصادر التي يشغلها الكائن فإن استهلك هذا

الكائن مصادر النظام بصورة كبيرة فإن هذه المسألة تصبح حرجة بالنسبة إلينا على كل حال هناك حل

لهذه المشكلة وسوف نتحدث عن كيفية التخلص من الكائنات الحقا في هذا الفصل.

أعضاء الصنف الستاتيكية:

Static Class Members:

وباإلضافة إلى إمكانية ان يكون لدينا أعضاء كالخصائص والمناهج والحقول المخصصة لحاالت محددة

( وتسمى أيضا باألعضاء staticأعضاء ستاتيكية ) من الكائنات إال أنه من الممكن أيضا أن يكون لدينا

( والتي يمكن ان تكون مناهج أو خصائص او حقول إن األعضاء الستاتيكية مشتركة sharedالمشتركة )

نات وكأنها أعضاء عامة لكائ نتأملهابين جميع الكائنات التي تنتمي إلى صنف واحد وبالتالي يمكننا ان

حقول الستاتيكية بالوصول إلى البيانات المستقلة عن اية حالة من صنف ما تسمح لنا الخصائص وال

الكائنات تمكننا المناهج الستاتيكية من تنفيذ األوامر المرتبطة بنوع الكائن وليس بحالة محددة من الصنف

ي ذوبالتالي إذا أردنا استخدام األعضاء الستاتيكية فإننا لسنا بحاجة إلى إنشاء حالة من كائنات الصنف ال

يحوي هذه األعضاء.

مناهج ستاتيكية ()Convert.ToStringو ()Console.WriteLineيمثل المنهجان على سبيل المثال

كي نتمكن من استخدام هذه Convertأو Consoleونحن لسنا بحاجة هنا إلنشاء حالة من األصناف

Page 22: مستويات مبتدئ و متوسط -ج2

- 21 - إعداد: المهندس حسام الدين الرز

ك ألن مناهج بناء هذه األصناف المناهج وإن حاولنا ذلك فإن هذا سيؤدي إلى حدوث خطأ في الترجمة وذل

خاصة )ليست عامة( كما شرحنا ذلك مسبقا.

هناك العديد من األوضاع التي يمكننا استخدام المناهج والخصائص الستاتيكية فيها.

كما في الشكل التالي: UMLيمكننا أن نمثل األعضاء الستاتيكية في الصنف بصيغة

(1-5الشكل )

توجه:تقنيات البرمجة كائنية ال

OOP Techniques:

لقد تناولنا حتى اآلن األساسيات وعرفنا ماهي الكائنات وكيف تعمل ولقد حان الوقت لكي نلقي نظرة على

بعض المزايا األخرى للكائنات سوف نتحدث في هذا القسم عن:

الواجهات.

الوراثة.

تعددية األشكال.

العالقات بين الكائنات.

التحميل الزائد للعوامل.

حداث.اال

Page 23: مستويات مبتدئ و متوسط -ج2

- 22 - إعداد: المهندس حسام الدين الرز

الواجهات:

Interfaces:

من المناهج والخصائص العامة المطلقة مجمعة مع بعضها مجموعةعلى أنها interfaceتعرف الواجهة

البعض بحيث تغلف وظيفة معينة ومتى عرفنا واجهة ما فإن بإمكاننا استخدامها ضمن صنف ما هذا يعني

أن الصنف سيزود بجميع خصائص ومناهج الواجهة.

أنه ال يمكن ان تتواجد الواجهات لوحدها فنحن ال يمكننا إنشاء حالة من الواجهة كما يمكننا ذلك مع الحظ

األصناف وباإلضافة إلى ذلك فإن الواجهات ال يمكن أن تتضمن أية شيفرة برمجية ضمن أعضائها فهي

لواجهة.لمزود بهذه اتحوي تعاريف األعضاء فقط وأما كتابة شيفرة هذه األعضاء فيتم عن طريق الصنف ا

بالعودة إلى مثال فنجان القهوة السابق يمكننا على سبيل المثال أن نجمع الخصائص والمناهج ذات

( ضمن واجهة ولتكن واجهة المشروبات Sugarو Milkو ()AddSugarاألغراض العامة مثل )

هذه الواجهة مع كائنات ( يمكننا استخدام Iتبدأ أسماء الواجهات عادة بالحرف IHotDrinkالساخنة

وبالتالي يمكننا أن نعامل هذه الكائنات بنفس الطريقة CupOfTeaأخرى ربما استخدامها مع الصنف

ويمكن لهذه الكائنات أن تكون لها خصائصها ومناهجها الخاصة أيضا )مثل خاصية نوع حبوب القهوة

BeanType لصنف فنجان القهوةCupOfCoffee لشاي أو خاصية نوع ورق اLeafType لصنف

(.CupOfTeaالشاي

CupOfTeaد الصنفان يتزو بتم وكيف IHotDrinkللواجهة UMLيمثل الشكل التالي تمثيل

(.1-6بها الحظ أن تمثيل الواجهة مشابه تماما لتمثيل الصنف الشكل ) CupOfCoffeeو

لواجهة وإن استخدامنا يمكن أن يدعم الصنف واجهات عديدة ويمكن ألصناف عديدة أن تزود بنفس ا

للواجهات في البرمجة يوفر علينا الكثير من الجهد فبدال من أن نعيد تعريف األعضاء المشتركة بين

صنفين يمكننا أن نضع هذه األعضاء ضمن واجهة ومن ثم نزود هذين الصنفين بهذه الواجهة وسيصبح

اء الخاصة لكل صنف على حدة.األمر وكأن لهذين الصنفين أعضاء الواجهة باإلضافة إلى األعض

توضيح:

فإن علينا أن نزود هذا Aماذا نقصد بالتزويد؟ عندما نقوم بإنشاء صنف ما يتضمن منهجا وليكن

فإنه ال يمكننا كتابة IAالمنهج بشيفرة معينة يقوم بها هذا المنهج أما عندما نقوم بإنشاء واجهة ولتكن

تعريف هذه المناهج فقط( عندما نود استخدام المناهج شيفرة لمناهجها )على الرغم من أنه يمكن

فإن علينا القيام بما يلي: Aفي الصنف IAالمعرفة ضمن الواجهة

تزويد الصنفA بالواجهةIA وذلك ضمن سطر تعريف الصنف.

تزويد الصنفA بالمناهج المقابلة الموجودة فيIA أي تعريف هذه المناهج وكتابة شيفرة

ضمنها.

Page 24: مستويات مبتدئ و متوسط -ج2

- 23 - إعداد: المهندس حسام الدين الرز

(1-6ل )الشك

التخلص من الكائنات:

Disposable Objects:

إن الكائن الذي يدعم هذه IDisposableإن إحدى الواجهات المهمة التي يجب أن ننوه عنها هنا هي

يمكن أن يستدعى هذا المنهج عندما ال نعود بحاجة ()Disposeالواجهة يجب أن يزود بمنهج يسمى بـ

ملية التخلص من الكائنات ليست ضرورية دائما مع جميع الكائنات وإنما للكائن ونود التخلص منه ولكن ع

نحتاج أحيانا للتخلص من الكائنات التي تستهلك قدرا كبيرا من موارد النظام وذلك بعد انتهاءنا من

استخدامها هل هذا يعني أن علينا تذكر استدعاء هذا المنهج دوما؟ في الحقيقة هناك أسلوب آخر يمكننا

امه للتخلص من الكائنات.استخد

من االستفادة القصوة من هذا المنهج بطريقة غير مباشرة تمكننا باستخدام بنية خاصة #Cتسمح لنا لغة

ل بشكيمكننا أن نهيئ كائنا يستخدم موارد حرجة )أي يستهلك موارد من النظام Usingفباستخدام الكلمة

من كتلة برمجية خاصة لها الصيغة التالية:كبير كالذاكرة مثال( حيث نستخدم هذا الكائن ض

سيستدعى تلقائيا عند الوصول إلى نهاية هذه الكتلة أي أن الكائن ()Disposeوبهذه الطريقة فإن المنهج

<VariableName> .سيستخدم ضمن كتلة الشيفرة تلك فقط وسيتم التخلص منه تلقائيا عند نهاية الكتلة

Using (<ClassName> <VariableName>=new <ClassName>())

{

}

Page 25: مستويات مبتدئ و متوسط -ج2

- 24 - إعداد: المهندس حسام الدين الرز

الوراثة:

Inheritance:

من أهم مزايا البرمجة كائنية التوجه حيث يمكن ألي صنف أن يرث من صنف Inheritanceالوراثة تعد

خر ومعنى ذلك أن األعضاء الموجودة في الصنف المورث كالخصائص والمنهج يمكن أن تستخدم في آ

الصنف الوارث )المشتق( كما لو كانت معرفة ضمنية ويطلق على الصنف المورث بالصنف األب

parent ( ويسمى أيضا بالصنف األساسbase أما األصناف التي ترث من الصنف األساس فتسمى )

من صنف أساس واحد سنتعرف عليه في #C( وتنحدر جميع الكائنات في derivedباألصناف المشتقة )

الفصل القادم.

يل المثال سب تمكننا الوراثة من توسعة األصناف أو إنشاء أصناف أكثر تخصصا من الصنف األساس على

ويتضمن مناهج مثل Animalلنفترض أن هناك صنف الحيوانات يمكننا أن نسمي هذا الصنف باالسم

EatFood() وBreed() يمكننا إنشاء صنف مشتق يسمى بصنف البقرcow حيث يدعم جميع المناهج

()SupplyMilkو ()Mooباإلضافة إلى أنه يدعم مناهجه الخاصة مثل Animalالمتوفرة في الصنف

()Cluckحيث يتضمن على المناهج Chickenيمكننا إنشاء صنف آخر مشتق وليكن صنف الدجاج

مثال. ()LayEggو

كما في الشكل التالي: UMLيمكننا تمثيل الوراثة بواسطة مخطط

(1-7الشكل )

Page 26: مستويات مبتدئ و متوسط -ج2

- 25 - إعداد: المهندس حسام الدين الرز

مالحظة:

لقد تجاهلت هنا أنواع القيم المعادة لتبسيط األمور ال أكثر.

األساس تصبح مسألة وصولية األعضاء أمرا هاما هنا ال يمكن الوصول من الصنف صنف وعندما يرث

إلى األعضاء الخاصة للصنف األساس من الصنف المشتق إال أنه يمكننا الوصول إلى األعضاء العامة

في الحقيقة يمكننا الوصول إلى األعضاء العامة من ضمن الصنف األساس نفسه أو من ضمن أي صنف

وكذلك من ضمن شيفرة خارجية لكن ماذا لو كنا بحاجة للوصول إلى األعضاء ضمن الصنف مشتق

المشتق دون التمكن من الوصول إليهم من الشيفرة الخارجية؟

حيث يمكن فقط protectedلتحقيق ذلك هناك نوع ثالث من أنواع الوصولية يسمى بالنوع المحمي أو

لمحمية في الصنف األساس.لألصناف المشتقة أن تصل إلى األعضاء ا

وباإلضافة إلى تعريف مستوى الحماية ألعضاء الصنف األساس فإن بإمكاننا أيضا تعريف سلوك الوراثة

بمعنى أنه يمكن تجاوز هذا virtualلهذه األعضاء فيمكن ألعضاء الصنف األساس أن تكون ظاهرية

أنه ربما يكون لدى الصنف المشتق تزويد من قبل الصنف المشتق إن ما نعنيه بذلك overriddenالعضو

بديل لهذا العضو إن التزويد لهذا العضو ال يلغي شيفرة العضو األصلي وإنما الوصول إليه من ضمن

يفرة ل لهذا العضو عندئذ ستتمكن الشيالصنف إال أن ذلك يحجبه عن الشيفرة الخارجية فإن لم يكن هناك بد

ف األساس لهذا العضو.الخارجية من الوصول إلى تزويد الصن

الحظ أن األعضاء الظاهرية ال يمكن أن تكون أعضاء خاصة ألن ذلك سيعتبر تناقضا فمن المستحيل أن

نقول أن العضو يمكن أن يتجاوز من قبل الصنف المشتق في حين ال يمكن الوصول إلى هذا العضو في

الصنف المشتق!

منهجا ظاهريا وذلك ألن بإمكان ()EatFoodالمنهج وبالعودة إلى مثال الحيوانات يمكننا ان نجعل

الحيوانات أن تأكل أشياء مختلفة وليس شيئا موحدا ومن ثم توفير تزويد جديد لهذا المنهج ضمن الصنف

(.1-8ق يمكننا تمثيل ذلك كما في الشكل )المشت

صنف واآلخر ضمن ال Animalأحدهما ضمن الصنف األساس ()EatFoodالحظ أن هناك منهجا

وذلك لكي يكون للصنف المشتق تزويده cowفي الصنف ()EatFoodلقد أضفنا المنهج cowالمشتق

الخاص لهذا المنهج.

ال يمكننا أن ننشئ حالة من أي abstractيمكننا ان نعرف األصناف األساس على أنها أصناف مجردة

رد ا أوال أن نورثه لصنف غير مجالصنف المجرد بصورة مباشرة ولكي نستخدم الصنف المجرد فإن علين

ويمكن لألصناف المجردة أن تحوي على أعضاء مجردة أيضا وبالتالي ليس لهذه األعضاء أي تزويد في

تزويد لهذه األعضاء ضمن الصنف المشتق. هناك أن يكون يجب الصنف األساس أي

ات كما في الشكل لمثال الحيوان UMLصنفا مجردا عندئذ سيصبح مخطط Animalsفإن كان الصنف

(9-1.)

Page 27: مستويات مبتدئ و متوسط -ج2

- 26 - إعداد: المهندس حسام الدين الرز

(1-8الشكل )

(1-9الشكل )

أي ال يمكننا استخدامه كصنف أساس وبالتالي ال يمكن ان sealedوأخيرا يمكن للصنف أن يكون منغلقا

نشتق منه أصنافا أخرى.

ويسمى بالصنف #Cوهو يمثل الصنف األساس لجميع الكائنات في #Cهناك صنف أساس مشترك في

object و اختصار لالسم الكامل وهSystem.object في إطار عمل.NET سوف نتحدث عن هذا

الصنف بتفصيل أكبر في الفصل القادم.

Page 28: مستويات مبتدئ و متوسط -ج2

- 27 - إعداد: المهندس حسام الدين الرز

مالحظة:

يمكن للواجهات التي تحدثنا عنها مسبقا في هذا الفصل أن ترث من واجهات أخرى وبعكس األصناف

التي يمكن لألصناف أن تزود فإن الواجهات يمكن أن ترث من واجهات أساس عديدة بنفس الصورة

بواجهات عديدة.

توضيح:

لماذا نستخدم الواجهات ما دامت وراثة األصناف تمكننا من القيام بنفس الوظائف تقريبا؟ حسنا ال بد

أن األمور بدأت تختلط عليك اآلن إال أننا سنبين متى نستخدم الواجهات ومتى نستخدم وراثة األصناف

في الفصل القادم.

ة األشكال:تعددي

Polymorphism:

إن احد األمور المهمة المتعلقة بالوراثة هنا هي أن الصنف المشتق يمكن أن يشترك مع الصنف األساس

بالمناهج والخصائص وبسبب ذلك فإنه من الممكن في معظم األحيان أن نعامل الكائن الناشئ من األصناف

على سبيل المثال إذا كان للصنف األساس المشتقة من صنف أساس مشترك باستخدام صيغة مشتركة

Animal منهجا باسمEatFood() عندئذ فإن صيغة استدعاء هذا المنهج من األصناف المشتقةcow و

Chicken :ستكون مماثلة أيضا الستدعاء المنهج كما لو كان منتميا إلى الصنف المشتق

cow myCow = new cow(); Chicken myChicken = new Chicken(); myCow.EatFood();

d();omyChicken.EatFo

إن تعددية األشكال هنا تعطي للوراثة بعدا جديدا فيمكننا أن نسند لمتحول من نوع الصنف األساس متحوال

من نوع الصنف المشتق كما يلي:

Animal myAnimal=myCow;

هنا يمكننا استدعاء مناهج الصنف األساس من خالل هذا castingكيل ولسنا بحاجة الستخدام التش

المتحول:

myAnimal.EatFood();

المزود في الصنف المشتق الحظ أنه ال يمكننا ()EatFoodسيؤدي هذا االمر إلى استدعاء المنهج

ورة صحيحة:لية لن تعمل بصاستدعاء المناهج المعروفة في الصنف المشتق بنفس الطريقة أي الشيفرة التا

myAnimal.Moo();

Page 29: مستويات مبتدئ و متوسط -ج2

- 28 - إعداد: المهندس حسام الدين الرز

هنا حيث يمكننا تشكيل متحول النوع األساس إلى متحول castingعلى كل حال يمكن أن نستخدم التشكيل

الصنف المشتق واستدعاء المنهج المعرف ضمن الصنف المشتق كما يلي:

cow myNewCow = (cow)myAnimal; myNewCow.Moo();

cowليس بالصنف myAnimalسيتسبب هذا النوع من التشكيل برفع اعتراض إذا كان نوع المتحول

هناك عدة طرق لمعرفة نوع الكائن إال أننا سنترك ذلك cowأو لم يكن أي صنف مشتق من الصنف

للفصل القادم.

مختلفة المتحدرة من صنف واحد.تمثل تعددية االشكال تقنية مفيدة جدا ألداء مهام على الكائنات ال

الحظ أنه ليس مجرد األصناف التي تشترك بصنف األب نفسه وبشكل مباشر يمكن أن تستخدم تعددية

األشكال فمن الممكن أن نعالج ولنفرض صنف االبن وصنف الجد بنفس الطريقة أيضا )كاستخدام تعددية

وذلك طالما أن هناك (creatureله االسم وصنف الكائنات الحية وليكن cowاالشكال بين صنف البقر

صنف مشترك ضمن هرمية الوراثة.

وهذا يعني objectمشتقة من الصنف األساس #Cوكمالحظة إضافية هنا تذكر أن جميع األصناف في

هو الصنف الجذر في هرمية وراثة األصناف وهذا يعني أن من الممكن أن objectأن الصنف األساس

.objectئنات كحاالت من الصنف تعالج جميع الكا

مالحظة:

استخدام أي نوع من البارامترات عند إنشاء نص ()Console.WriteLineلهذا السبب يمكن لألمر

مما سيسمح بطباعة القيمة objectالخرج فكل بارامتر بعد البارامتر األول سيعالج كمتحول من نوع

المباشرة ألي كائن على نافذة الخرج.

شكال في الواجهات:تعددية األ

Interface Polymorphism:

لقد تحدثنا مسبقا عن مفهوم الواجهات ورأينا أنها تستخدم لتجميع المناهج والخصائص معا وعلى الرغم

من أنه من غير الممكن إنشاء حاالت من الواجهات إال انه يمكن أن يكون لدينا متحول من نوع الواجهة

متحول للوصول إلى المناهج والخصائص المقدمة من قبل هذه الواجهة على يمكننا عندئذ استخدام هذا ال

الكائنات التي تدعم تلك الواجهة.

()EatFoodلتزويد المنهج Animalعلى سبيل المثال لنفترض أنه بدال من استخدام الصنف األساس

Chickenين عندئذ يمكن للصنف ICoonsumeضمن واجهة تسمى بـ ()EatFoodفإننا سنضع المنهج

التزود بهذه الواجهة والفرق الوحيد في هذه الحالة هو أننا مجبرين على إيجاد تزويد للمنهج cowو

Page 30: مستويات مبتدئ و متوسط -ج2

- 29 - إعداد: المهندس حسام الدين الرز

EatFood() ضمن الصنف باعتبار أن الواجهات ال تتضمن أية تزويدات يمكننا عندئذ الوصول إلى هذا

المنهج باستخدام شيفرة كما يلي:

cow myCow = new cow(); Chicken myChicken = new Chicken(); IConsume consumeInterface; consumeInterface=myCow; consumeInterface.EatFood(); consumeInterface=myChicken; consumeInterface.EatFood();

دة بنفس األسلوب حيث ال تعتمد على صنف أساس مشترك يوفر ذلك طريقة بسيطة الستدعاء كائنات عدي

والذي سيؤدي إلى ()consumeInterface.EatFoodلقد قمنا في هذه الشيفرة باستدعاء المنهج

باالعتماد تباعا وذلك Chickenوالصنف cowالموجود ضمن الصنف ()EatFoodاستدعاء المنهج

على متحول الحالة المسند إلى متحول الواجهة.

العالقات بين الكائنات:

Relationship between Objects:

-تمثل الوراثة عالقة بسيطة بين الكائنات حيث يتم كشف الصنف األساس بصورة كاملة للصنف المشتق

للصنف المشتق أن يتمكن من الوصول الداخلي إلى بعض أعضاء الصنف ويمكن -منهاألعضاء العامة

تكون فيها العالقات بين الكائنات أمرا مهما.عديدة مية هناك أوضاع األساس من خالل األعضاء المح

سوف نتناول في هذا القسم ما يلي:

لوراثة إال أن االحتواء يمكن لصنف ما صنفا آخر إن هذا مشابه االحتواء: أي عندما يحتوي

قيام لالصنف الحاوي من التحكم بالوصول إلى أعضاء الصنف المحتوى باإلضافة إلى إمكانية ا

بعمليات إضافية قبل استخدام أعضاء الصنف المحتوى.

كأنه حاو لحاالت عديدة من صنف آخر إن هذا مشابه لحصولنا حيث يعامل الصنف و المجموعات:

على مصفوفة من الكائنات إال أن للمجموعات مزايا أخرى تمتاز بها عن المصفوفات فهي تتضمن

الحجم وغيرها من المزايا. آلية للفهرسة والترتيب وإمكانية تغيير

االحتواء:

Containment:

إن تطبيق االحتواء أمر بسيط جدا ويتم باستخدام حقل ما الستيعاب حالة كائن يمكن لهذا الحقل أن يكون

عاما وبالتالي سيتمكن مستخدمو الكائن الحاوي من الوصول إلى مناهج وخصائص الكائن الموجود ضمن

Page 31: مستويات مبتدئ و متوسط -ج2

- 30 - إعداد: المهندس حسام الدين الرز

قل إن هذا مشابه للوراثة إال أننا هنا لن نتمكن من الوصول إلى األعضاء الكائن الحاوي على شكل ح

الداخلية للصنف المحتوى عبر الصنف المشتق كما كنا نستطيع ذلك بالوراثة.

مالحظة:

تذكر أن ما نقصده باألعضاء الداخلية هي األعضاء العامة والمحمية فقط فاألعضاء الخاصة في الصنف

ها من الخارج.ال يمكننا الوصول إلي

وبصورة بديلة فإنه يمكننا جعل الكائن المحتوى عضوا خاصا ضمن الصنف الحاوي وإذا قمنا بذلك فلن

نتمكن من الوصول إلى أي من أعضاء هذا الكائن مباشرة من خالل الشيفرة الخارجية وحتى إن كانت

إلى هذه األعضاء باستخدام أعضاء الكائن المحتوى عامة وبدال من ذلك فإن بإمكاننا ان نوفر وصوال

أعضاء الصنف الحاوي هذا يعني أن لدينا تحكما كامال في كشف أعضاء الكائن المحتوى ضمن الصنف

الحاوي باإلضافة إلى القيام بعمليات إضافية في أعضاء الصنف الحاوي قبل الوصول إلى أعضاء الصنف

المحتوى.

والذي له منهج عام Udderمن على صنف الضرع أن يتض cowعلى سبيل المثال يمكن لصنف البقر

أن يستخدم هذا المنهج حسب الحاجة ولربما يقوم باستدعاء هذا cowيمكن لكائن ()Milkيسمى بالحليب

إال ان تفاصيل هذا المنهج لن تكون ظاهرة أو مهمة لمستخدمي ()SupplyMilkالمنهج كجزء من المنهج

.cowالكائن

نوع الصنف باستخدام خط رابط بين الخاصية من UMLاف المحتواة في تمثيل يمكننا ان نمثل األصن

وذلك لإلشارة إلى أنها عالقة 1نفسه وسنضع على طرفي هذا الخط الرقم يالمحتوى والصنف المحتو

واحد يمكننا أن نستعرض كائن Udderسيتضمن على كائن cowواحد أي أن كائن –إلى –واحد

وذلك للتوضيح: cowقل خاص في الصنف وكأنه ح Udderالصنف

(1-10الشكل )

Page 32: مستويات مبتدئ و متوسط -ج2

- 31 - إعداد: المهندس حسام الدين الرز

المجموعات:

Collections:

لقد تعلمنا في الفصل الخامس كيفية استخدام المصفوفات لحفظ قيم عديدة من نفس النوع في متحول واحد

يضا أإن ذلك ينطبق أيضا على الكائنات تذكر أن أنواع المتحوالت التي استخدمناها هي عبارة عن كائنات

على سبيل المثال:

Animal [] animals = new Animal[5];

إن المجموعات عبارة عن مصفوفة مع بعض المزايا اإلضافية تسمى المجموعات عادة بصيغة الجمع

أن يحتوي على مجموعة من Animalsللكائنات التي تحتويها على سبيل المثال يمكن للصنف ذي االسم

.Animalكائنات

الفرق الرئيسي بين المجموعات والمصفوفات يكمن في أن المجموعات تتضمن وظائف إضافية مثل إن

إلضافة وإزالة العناصر من المجموعة ويكون هناك عادة الخاصية ()Removeو ()Addالمنهجين

Item لحقيقة اوالتي تمكننا من الوصول إلى كائن ما في المجموعة باالعتماد على موقعه )دليله( منها. في

هناك طرق تمكننا من استخدام هذه الخاصية للوصول إلى العناصر بصورة أكثر تفاعلية فعلى سبيل

وفقا ألسمائها. Animalبحيث نتمكن من الوصول إلى كائنات Animalsالمثال يمكننا تصميم المجموعة

التالي المجموعات وارتباطها بعناصرها: UMLيبين تمثيل

(1-11الشكل )

حظة:مال

لم أضع األعضاء هنا وذلك ألننا مهتمين اآلن بالعالقة بين الكائنات.

Page 33: مستويات مبتدئ و متوسط -ج2

- 32 - إعداد: المهندس حسام الدين الرز

يمكن Animalsتبين األرقام في أطراف الخط الواصل بين صنف المجموعة وصنف كائناتها بأن الكائن

.Animalويمكن أن يتضمن أي عدد من كائنات Animalأال يتضمن أي كائن

.الرابع من هذا الجزءبر في سوف نتحدث عن المجموعات بتفصيل أك

التحميل الزائد للعوامل:

Operator Overloading:

ي الفصول السابقة من هذا الكتاب لمعالجة أنواع المتحوالت البسيطة لكن قد نجد لقد استخدمنا العوامل ف

نا إن سفي بعض األحيان أنه من المنطقي استخدام العوامل مع كائنات األصناف التي نقوم بإنشائها بأنف

ذلك ممكن وذلك بتزويد األصناف بتعليمات تبين كيفية تفسير نتائج هذه العوامل.

عندئذ إذا أردنا مقارنة Weightباسم Animalعلى سبيل المثال يمكننا أن نضيف خاصية إلى الصنف

أوزان الحيوانات باستخدام هذه الخاصية فإننا سنكتب شيفرة كما يلي:

if (cowA.Weight > cowB.Weight) { ...

}

في مقارنة Weightباستخدام التحميل الزائد للعوامل يمكننا أن نوفر المنطق الذي يعتمد على الخاصية

أوزان الحيوانات باستخدام أسماء الكائنات مباشرة وبالتالي يمكننا أن نكتب الشرط السابق كما يلي:

if (cowA > cowB) { ...

}

overloaded" والعامل المحمل بصورة زائدة <للعامل" الزائدلقد قمنا هنا باستخدام تقنية التحميل

operator ي تهو العامل الذي نكون قد كتبنا شيفرة تحدد سلوكه وذلك كجزء من تعريف أحد األصناف ال

" <والعامل هو" cowهما كائنان من الصنف هتستخدمه فلقد استخدمنا في الشيفرة السابقة تعبيرا حدي

الحظ أن هذا النوع من العمليات غير منطقي إال إذا كان هناك تعريف جديد لهذا العامل يجعله منطقيا.

روفة فقط فال يمكننا ان ننشئ المع #Cمع عوامل الزائدوالحظ أيضا أن بإمكاننا استخدام تقنية التحميل

لهذه العوامل بشكلي هذه العوامل األحادي والثنائي. الزائدعوامل جديدة إال أنه يمكننا أن نستخدم التحميل

.في الرابع من هذا الجزءسوف نتحدث عن ذلك

األحداث:

Events:

Page 34: مستويات مبتدئ و متوسط -ج2

- 33 - إعداد: المهندس حسام الدين الرز

ا فهي عبارة عن شيفرة ملها إن االحداث مهمة جدكجزء من ع eventsيمكن للكائنات ان تشهر االحداث

برمجية معينة تنفذ عند حصول ظرف معين وهي مشابهة لطريقة عمل االعتراضات إال أنها أكثر قوة

جديد إلى مجموعة Animalمنها يمكننا على سبيل المثال تأدية مهمة معينة عندما نقوم بإضافة كائن

أو الشيفرة التي Animalsمن الصنف بحيث ال تمثل الشيفرة التي تقوم بذلك جزءا Animalsالكائنات

لشيفرتنا ومعالج event handlerوللقيام بذلك فإن علينا إضافة معالج حدث ()Addتستدعي المنهج

الحدث عبارة عن نوع خاص من التوابع التي تستدعى عند حدوث ذلك الحدث وهو ما نسميه عندئذ

الج الحدث هذا لكي يستمع إلى الحدث الذي وعلينا أن نعد مع raise eventبإشهار الحدث أو رفعه

يخصه.

وهي أكثر تعقيدا مما تتوقع على سبيل المثال من event-drivenيمكننا إنشاء تطبيقات مقادة باألحداث

تعتمد بصورة كلية على األحداث فكل ضغطة بزر Windowsالمهم أن تعلم أن العديد من تطبيقات

إلى رفع حدث ما تتم معالجته بواسطة معالج الحدث الخاص به الفأرة أو سحب لشريط التمرير يؤدي

وهذا يعني أنه يمكننا توليد األحداث من خالل الفأرة او لوحة المفاتيح أيضا.

وسوف نتعمق في Windowsسوف نرى الحقا في هذا الفصل كيفية عمل آلية الحدث في تطبيقات

.ث عن األحداث في الخامس من هذا الجزءالحدي

ع المرجع مقابل أنواع القيمة:أنوا

Reference vs. Value Types:

بطريقتين وذلك بحسب نوع المتحول ويمكننا اآلن أن نصنف األنواع في #Cالمتحول في ظ البيانات بتحف

C# ( تحت بندين: أنواع المرجعReference Types( وأنواع القيمة )Value Types:)

يسمى بالمكدس نحفظ أنواع القيمة محتواها ضمن موضع(واحد في الذاكرةStack.)

تحفظ أنواع المرجع بمرجع يشير إلى محتوى موجود في مكان آخر من الذاكرة )يسمى

(.Headبالكومة

وهي Stringلقد استخدمنا متحوالت #Cأنت لست بحاجة لالهتمام لهذه التفاصيل عند البرمجة بلغة

ومعظمها أنواع قيمة وقد كان استخدامنا لهذين intوالت أنواع مرجعية ومتحوالت أخرى بسيطة مثل متح

النوعين متشابها.

وهناك أيضا objectوالنوع Stringفي الحقيقة ليس هناك إال نوعين مرجعيين بسيطين فقط: النوع

المصفوفات والتي تعتمد بطبيعتها على النوع األساس لها إن كل صنف تقوم بإنشائه هو نوع مرجعي ولذا

نوهت عن ذلك هنا في هذا الفصل.فإنني

Page 35: مستويات مبتدئ و متوسط -ج2

- 34 - إعداد: المهندس حسام الدين الرز

:Structالبنية

Struct:

بيهة جدا باألصناف إال أن ش Structهناك نقطة هامة يجدر اإلشارة إليها هنا فعلى الرغم من أن البنية

األصناف كما قلنا فهي نوع مرجعي. نوع قيمة وأما Structهناك فرقا جوهريا بينهما فالبنية

مالحظة:

كل منهما أعضاء ويمكن لكل منهما أن لواضح بالنسبة إلينا ف Structاألصناف وبنى إن التشابه بين

يحتوي على توابع تسمى بالمناهج في األصناف ومتحوالت أيضا تسمى بالخصائص أو الحقول في

األصناف.

:Windowsالبرمجة كائنية التوجه في تطبيقات

OOP in Windows Applications:

إن #Cبلغة Windowsكيفية إنشاء تطبيق من الجزء األول لهذا الكتاب الثاني لقد رأينا في الفصل

وسوف نتناول في هذا القسم بعضا من OOPتعتمد بصورة كبيرة على تقنيات Windowsتطبيقات

النقاط التي تضع ما تحدثنا عنه في هذا الفصل موضعا عمليا.

:Windowsتطبيق حول الكائنات في تطبيقات

Windowsجديد وقم بحفظه باسم Windows Applicationشاء تطبيق قم بإن -1

Application OOP.

وضعه في وسط النموذج Toolboxدوات األ تخدام صندوقباس Buttonأضف عنصر تحكم -2

Form1:

Page 36: مستويات مبتدئ و متوسط -ج2

- 35 - إعداد: المهندس حسام الدين الرز

(1-12الشكل )

انقر نقرا مزدوجا على زر االمر إلضافة الشيفرة لحدث الضغط على هذا الزر بالفأرة عدل -3

رة كما يلي:الشيف

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace Windows_Application_OOP { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { ((Button)sender).Text = "اضغط الزر"; Button newButton = new Button(); newButton.Text = "زر جديد"; newButton.Click += new EventHandler(newButton_Click); Controls.Add(newButton); }

Page 37: مستويات مبتدئ و متوسط -ج2

- 36 - إعداد: المهندس حسام الدين الرز

private void newButton_Click(object sender, System.EventArgs e) { ((Button)sender).Text = "اضغط الزر"; } } }

من شريط األدوات Startمن لوحة المفاتيح أو بالضغط على زر F5نفذ التطبيق بالضغط على -4

(.1-12فيظهر الشكل )

(1-12الشكل )

نا" ويظهر زر جديد باسم "زر فيتغير اسم الزر إلى "اضغط ه Button1اضغط على الزر -5

(.1-13جديد" كما في الشكل )

(1-13الشكل )

Page 38: مستويات مبتدئ و متوسط -ج2

- 37 - إعداد: المهندس حسام الدين الرز

(.1-14انقر على الزر "زر جديد" والحظ كيف يتغير اسمه إلى "اضغط هنا" كما في الشكل ) -6

(1-14الشكل )

كيفية العمل:

How to Work:

عمل ما ولقد استخدمنا بعضا يقوم ب Windowsبإضافة بعض األسطر البرمجية فإننا قمنا بإنشاء تطبيق

أيضا في الحقيقة "كل شيء عبارة عن كائن!" هي عبارة حقيقية جدا عند التعامل مع OOPمن تقنيات

وجودة اصر التحكم المض عند تنفيذ التطبيق وصوال إلى عنفمن النموذج الذي سيعر Windowsتطبيقات

وعلى OOPما وسوف نركز هنا على تقنيات دائ OOPعلى النموذج فإن جميعها تحتاج الستخدام تقنيات

المفاهيم النظرية التي تحدثنا عنها في هذا الفصل لتتجلى أمامنا الصورة بشكل أوضح.

إن هذا الزر هو عبارة عن Form1إن أول ما قمنا به في تطبيقنا هو إضافة زر أمر جديد إلى النموذج

قر المزدوج على هذا الزر فإننا نكون قد وبالن Buttonوهو كائن من الصنف Button1كائن يسمى

سيتم إضافة معالج Buttonعلى الزر Clickستنفذ عند النقر event handlerأضفنا معالج حدث

:privateالذي يغلف تطبيقنا وذلك كمنهج خاص Form1الحدث هذا إلى الشيفرة ضمن الكائن

private void button1_Click(object sender, EventArgs e) { }

في بداية تعريف المنهج ال تقلق حول ذلك اآلن فسوف نتحدث عما تعنيه privateلقد استخدمنا هنا الكلمة

هذه الكلمة وغيرها الحقا في الفصل القادم.

دية األشكال إن السطر األول الذي أضفناه هنا يقوم بتعديل نص عنوان الزر ولقد استخدمنا هنا تعد

polymorphism يمثل الكائنButton1 الزر الذي إذا نقرنا عليه بزر الفأرة فإنه سيتم إرسال هذا

Page 39: مستويات مبتدئ و متوسط -ج2

- 38 - إعداد: المهندس حسام الدين الرز

هذا الكائن إلى castحيث سنقوم بتشكيل objectمن نوع بارامترالحدث إلى معالج الحدث على شكل

والذي يمثل stem.objectSyيرث من الكائن Buttonإن ذلك ممكن باعتبار أن الكائن Buttonالنوع

لهذا الكائن وذلك لتغيير Textلقد غيرنا بعد ذلك قيمة الخاصية #Cفي objectاالسم الكامل للكائن

عنوان ذلك الزر كما يلي: ((Button)sender).Text = "اضغط الزر";

الشيفرة التالية حيث سيأخذ وذلك ضمن newجديد باستخدام الكلمة Buttonبعد ذلك قمنا بإنشاء كائن

زر األمر الجديد العنوان "زر جديد"

Button newButton = new Button(); newButton.Text = "زر جديد";

توضيح:

الحظ أننا استفدنا هنا من ميزة فضاءات األسماء وذلك لتبسيط الصيغ ويمكنك مالحظة هذا ضمن األسطر

تلك فإننا مضطرين إلى استخدام الصيغة usingإن لم نستخدم تعليمات Form.csملف األولى من ال

كما يلي: Buttonالكاملة وبالتالي فإن علينا كتابة النوع

.ButtonSystem.Windows.Forms

newButtonر على زر ما سنستخدمه لالستجابة لحدث النق هناك معالج حدث آخر في هذه الشيفرة وهو

الجديد:

private void newButton_Click(object sender, System.EventArgs e) { ((Button)sender).Text = "اضغط الزر";

}

األصلي فإننا قمنا بتسجيل معالج الحدث للزر Button1وبالعودة إلى شيفرة معالج حدث النقر على الزر

جديد EventHandlerالتحميل الزائد للعوامل ولقد قمنا بإنشاء كائن من نوع الجديد باستخدام صيغة

باستخدام منهج بناء غير افتراضي وذلك باستخدام نفس اسم منهج معالج الحدث الجديد:

(newButton_Click);EventHandler newnewButton.Click +=

للنموذج وذلك إلضافة الزر الجديد إلى النموذج Controlsوأخيرا فلقد استخدمنا كائن المجموعة

:()Addباستخدام المنهج

Controls.Add(newButton);

يبين هذا المثال البسيط أننا استخدمنا معظم تقنيات البرمجة كائنية التوجه التي تحدثنا عنها في هذا الفصل

لى درجة كبيرة وإنما تتطلب منا نظرة مختلفة ليست معقدة إ OOPوكما ترى من هذا المثال فإن تقنية

لطريقة البرمجة ال أكثر.

Page 40: مستويات مبتدئ و متوسط -ج2

- 39 - إعداد: المهندس حسام الدين الرز

الخالصة:

Summary:

في سياق OOPلقد قدم هذا الفصل وصفا كامال لتقنيات البرمجة كائنية التوجه لقد تحدثنا عن تقنيات

لكيفية من تطرقنا أكثربصورة خاصة OOPإال اننا ركزنا هنا على #Cحديثنا عن البرمجة بلغة

.#Cاستخدامها في

وكيفية إنشاء حالة من الكائن لصنف ما ثم objectلقد بدأنا أوال بتغطية األساسيات مثل معنى المصطلح

والخصائص fieldsرأينا بعد ذلك كيف يمكن للكائنات أن تتضمن أعضاء عديدة مثل الحقول

properties والمناهجmethods هذه األعضاء فيمكن ان تكون أعضاء وكيفية التحكم في الوصولية إلى

عامة او خاصة او محمية بعد ذلك تحدثنا عن إمكانية ان تكون هذه األعضاء ظاهرية او مجردة ولقد

.instanceمشتركة أو حالية staticتحدثنا أيضا عن إمكانية وجود أعضاء ستاتيكية

إلنشاء الكائنات Constructorsج البناء تحدثنا بعد ذلك عن دورة حياة الكائن ورأينا اننا نستخدم مناه

لحذف الكائنات ولقد تحدثنا أيضا حول كيفية تجميع األعضاء في واجهات Destructorsومناهج التدمير

interfaces وتحدثنا عن كيفية استخدام طرق أخرى للتخلص من الكائناتdisposable objects

.IDisposableبواسطة الواجهة

ورأينا كيف يمكن لألصناف أن ترث من أصناف أخرى ورأينا inheritanceالوراثة ولقد تحدثنا عن

وذلك من خالل األصناف األساس والواجهات polymorphismأيضا كيفية استخدام تعددية االشكال

المشتركة ورأينا كيف يمكننا ان نستخدم الكائنات الحتواء كائنات أخرى وذلك من خالل تقنيات االحتواء

containment والمجموعاتcollections وأخيرا فقد تحدثنا عن تقنية التحميل الزائد للعوامل لتبسيط

.eventsصيغ استخدام العوامل مع الكائنات وكيف يمكن للكائنات أن ترفع االحداث

لقد وضع الجزء األخير من هذا الفصل المعلومات النظرية التي تناولناها منذ بداية هذا الفصل موضع

Windowsالستخدام العملي على الرغم من أننا لم نستخدم إال بضعة أسطر برمجية وذلك ضمن تطبيق ا

بسيط جدا.

ربما لم تستوعب معظم ما تناولناه في هذا الفصل إال أن الهدف من تقديم جميع تقنيات البرمجة كائنية

روري ان تستوعب كل ما التوجه في فصل كهذا إلعطائك فكرة عامة عن هذه التقنيات وليس من الض

تحدثنا عنه هنا إال ان األمور ستتوضح لك بعد قراءتك للفصول األربعة القادمة.

Page 41: مستويات مبتدئ و متوسط -ج2

- 40 - إعداد: المهندس حسام الدين الرز

لثانيالفصل ا

تعريف األصناف

وسوف نتناول في هذا الفصل بعضا OOPلقد تحدثنا في الفصل السابق عن مزايا البرمجة كائنية التوجه

سابقا بتطبيقات عملية وموضوع هذا الفصل هو كيفية تعريف من المعلومات النظرية التي تطرقنا إليها

.#Cواستخدام األصناف في

لن نتحدث هنا عن كيفية تعريف أعضاء األصناف وسنركز على تعريف األصناف فقط وسنترك الحديث

عن األعضاء للفصل القادم.

تخدمها لتحديد احية التي سنسوكبداية فإننا سنتحدث عن الصيغة األساسية لتعريف األصناف والكلمات المفت

..الخ كما وسنتناول موضوع الوراثة بالتفصيل في هذا الفصل وسنتحدث أيضا عن .وصولية الصنف

الواجهات وكيفية تعريفها باعتبارها مشابهة لألصناف أما بقية هذا الفصل فسوف نتحدث ضمنه عن

يتضمن ذلك: #Cمواضيع عديدة متعلقة بتعريف األصناف في

الصنف System.object.

أدوات مفيدة يوفرهاVisual Studio.NET.

.مكتبات األصناف

.مقارنة بين الواجهات واألصناف المجردة

أنواع البنىStruct.

.نسخ الكائنات

:#Cتعريف األصناف في

Class Definitions in C#:

ة لتعريف األصناف لها الصيغ لتعريف األصناف والبنية األساسية Classالكلمة المفتاحية #Cتستخدم لغة

التالية:

Class <ClassName>

{

Page 42: مستويات مبتدئ و متوسط -ج2

- 41 - إعداد: المهندس حسام الدين الرز

//Class members

}

بتعريف الصنف فإن بإمكاننا اهنا إلى اسم الصنف الذي نود إنشائه ومتى قمن <ClassName>يشير

إنشاء كائن من هذا الصنف في أي مكان من مشروعنا يستطيع الوصول إلى هذا الصنف ويتم التصريح

بصورة افتراضية مما يعني أن شيفرة المشروع الحالي فقط يمكنها internalصناف داخليا عن األ

كما يلي: internalالوصول إلى هذا الصنف ويمكننا أن نحدد ذلك بشكل صريح باستخدام الكلمة

internal class MyClass { //Class members }

.MyClassيد باسم حيث قمنا في هذه الشيفرة بإنشاء صنف داخلي جد

وإذا أردنا ان نستخدم األصناف في شيفرة مشاريع أخرى فإن علينا التصريح عن الصنف على أنه صنف

كما يلي: Publicعام وذلك باستخدام الكلمة

public class MyClass { //Class members }

وذلك ألنها تحدد access modifiersبمقيدات الوصول OOP في publicو internalمى الكلمات تس

كيفية الوصول إلى األصناف أو أية عناصر أخرى.

مالحظة:

أو محمية privateالحظ أن األصناف المصرح عنها بهذه الصورة ال يمكن أن تكون خاصة

protected في صنف محتواهفمن الممكن استخدام هذين المقيدين للتصريح عن األصناف كأعضاء

لفصل القادم.وهو ما سنتحدث عنه في ا

)حاالت( من األصناف التي نعرفها بأنفسنا بصورة افتراضية ويمكننا تغيير هذا تيمكننا إنشاء كائنا

السلوك باستخدام مقيدات وصول معينة فكما يمكننا تحديد مدى الوصول للصنف ضمن تعريفه فإنه يمكننا

الكلمتين موللقيام بذلك فإننا سنستخد sealedأو منغلقا abstractأيضا تحديد ما إذا كان الصنف مجردا

abstract وsealed.

توضيح:

ونقصد بالصنف المجرد هو الصنف الذي ال يمكننا إنشاء حالة منه وإنما يمكننا توريثه ألصناف أخرى

فقط أما الصنف المغلق فهو الصنف الذي ال يمكن ان ترثه أصناف أخرى.

Page 43: مستويات مبتدئ و متوسط -ج2

- 42 - إعداد: المهندس حسام الدين الرز

فإننا سنعرف الصنف بالصورة MyClassلة من صنفنا العام وبالتالي إذا أردنا منع إمكانية إنشاء حا

التالية:

public abstract class MyClass { //Class members, may be abstract }

ويمكننا أن نصرح عن صنف مجرد داخلي MyClassهنا بالتصريح عن صنف مجرد عام باسم القد قمن

بنفس األسلوب.

بنفس الطريقة أيضا: أما األصناف المنغلقة تعرف

public sealed class MyClass { //Class members }

وكما هو الحال بالنسبة لألصناف المجردة فإن األصناف المنغلقة يمكن أن تكون داخلية أو عامة.

وإذا أردنا ان يرث صنفنا أعضاء صنف آخر فإن علينا تحديد ذلك ضمن سطر التصريح عن الصنف

بذلك فإننا سنضع الرمز ":" بعد اسم الصنف ونتبعه باسم الصنف األساس الذي سيرث منه أيضا وللقيام

مباشرة على سبيل المثال:

Public Class MyClass :MyBase

{

//Class members

}

وهو قابل ألن نقوم بإنشاء حاالت منه ويرث هذا الصنف MyClassلقد قمنا هنا بتعريف صنف عام باسم

.MyBaseأساس له االسم أعضاء صنف

وإذا ورث من صنف مجرد فإن علينا تزويد #Cالحظ أنه ال يمكن للصنف وراثة إال صنف واحد فقط في

صنفنا بجميع األعضاء المجردة الموجودة في الصنف األساس إال إذا كان صنفنا المشتق هو أيضا صنف

مجرد.

وصول أكبر من مدى وصول الصنف األساس لن يسمح لنا المترجم بأن يكون الصنف المشتق ذو مدى

إن هذا يعني أن الصنف الداخلي يمكن ان يرث من صنف أساس عام إال أنه ال يمكن لصنف عام أن يرث

من صنف أساس داخلي أن هذا يعني أن الشيفرة التالية صحيحة:

public class MyBase { //Class members

Page 44: مستويات مبتدئ و متوسط -ج2

- 43 - إعداد: المهندس حسام الدين الرز

} internal class MyClass : MyBase { //Class members }

إال ان الشيفرة التالية لن تترجم:

internal class MyBase { //Class members } public class MyClass : MyBase { //Class members }

فقط والذي System.Objectوعندما ال يرث الصنف أي صنف أساس أخر فهو يرث الصنف األساس

ترث الصنف األساس #Cفقط في الحقيقة إن جميع األصناف في objectالمختصر يمكننا كتابته باالسم

System.object بصورة افتراضية فهو يعد الجذر في هرمية وراثة األصناف سوف نتحدث عن هذا

الصنف األساسي الحقا في هذا الفصل.

مها صنفنا اجهات التي يدعيد الونه يمكننا تحديقة فإوباإلضافة إلى إمكانية تحديد أصناف األساس بهذه الطر

وإن كان صنفنا يرث من صنف أساس ومزود بواجهة أيضا فإن علينا ذكر اسم الصنف ":"ز بعد الرم

الحظ انه يمكن للصنف ","األساس أوال ثم اسم الواجهة بحيث يفصل بين االسمين بواسطة الفاصلة

التزود بعدة واجهات.

كما يلي: MyClassهة للصنف على سبيل المثال يمكننا إضافة واج

public class MyClass : IMyInterface { //Class members }

يجب أن يزود الصنف بجميع أعضاء الواجهة التي يدعمها ولو كان هذا التزود فارغا أي بدون شيفرة

مفيدة إذا لم نرغب باستخدام عضو واجهة معين.

عندئذ فإن التصريح MyBaseوصنفا أساس باالسم IMyInterfaceلنفرض ان لدينا واجهة باالسم

التالي غير صحيح:

public class MyClass : IMyInterface , MyBase { //Class members }

والطريقة الصحيحة للتصريح السابق بالصورة التالية:

public class MyClass :MyBase , IMyInterface {

Page 45: مستويات مبتدئ و متوسط -ج2

- 44 - إعداد: المهندس حسام الدين الرز

//Class members }

ن بإمكاننا تزويد الصنف بالعديد من الواجهات لذا فإن التصريح التالي صحيح:وتذكر أ

public class MyClass :MyBase , IMyInterface ,IMySecondInterface { //Class members }

أي يمكننا وضع صيغة عامة لتعريف األصناف كما يلي:

[<Mod1>] [<Mod2>] Class <ClassName> : [<BaseClass> , <Interfaces>]

{

// Class members

}

فتمثل أحد مقيدي الوصول <Mode1>إلى أن هذا الجزء من الصيغة اختياري أما "[]"تشير االقواس

internal أوpublic و<Mode2> فيمثل أحد مقيدي الوصولabstract اوsealed اما

<ClassName> مية المتحوالت في فيمثل اسم الصنف وهو يخضع لنفس قواعد تسC# أما

<BaseClass> فيمثل الصنف األساس الذي يرثه الصنف<ClassName> وكذلك فإن

<Interface> .يمثل الواجهات التي يتزود بها الصنف

يبين الجدول التالي االحتماالت الممكنة لمقيدات الوصول في تعريف الصنف:

المعنى المقيد

Internal أو بدون مقيد ول إلى الصنف ضمن المشروع يمكن الوص

.الحالي فقط

Public سمكن الوصول إلى الصنف من أي مكان ضمن

.المشروع الحالي او خارجه

abstract اوinternal abstract

يمكن الوصول إلى الصنف من ضمن المشروع

الحالي فقط وال يمكن إنشاء حالة من هذا الصنف

وإنما يمكننا توريثه ألصناف أخرى.

Public abstract يمكن الوصول إلى الصنف من أي مكان وال يمكن

إنشاء حالة من هذا الصنف وإنما يمكننا وراثته.

Sealed أوinternal sealed

يمكن الوصول إلى الصنف من ضمن المشروع

الحالي فقط وال يمكننا وراثة هذا الصنف وإنما

يمكننا إنشاء حالة منه.

Public sealed ل إلى الصنف من أي مكان وال يمكننا يمكن الوصو

وراثة هذا الصنف وإنما يمكننا إنشاء حالة منه.

Page 46: مستويات مبتدئ و متوسط -ج2

- 45 - إعداد: المهندس حسام الدين الرز

تعريف الواجهات:

Interface Definitions:

بدال من interfaceيصرح عن الواجهات بطريقة مشابهة للتصريح عن األصناف إال أننا نستخدم الكلمة

الصيغة التالية: أي التصريح البسيط عن الواجهة يأخذ Classالكلمة

Interface <interfaceName>

{

// interface members

}

األسلوب التي تستخدمه األصناف أيضا وبالتالي يمكننا بنفس internalو publicويستخدم مقيدا الوصول

من خارج المشروع الحالي باستخدام التعريف IMyInterfaceان نتمكن من الوصول إلى الواجهة

التالي:

public interface IMyInterface { // interface members }

الواجهات وذلك ألنه ليس الستخدامها أي معنى مع مع sealedو abstractال يمكننا استخدام المقيدات

الواجهات ذلك ألنه ال يمكننا استخدام الواجهات بصورة مباشرة وإنما يجب أن نورثها لصنف أو لواجهة

د منها.أخرى كي نستفي

ويمكن للواجهات ان ترث واجهات أخرى بنفس أسلوب الوراثة في األصناف إال أن الفرق األساسي هنا

يكمن في عدم وجود صنف أساس على سبيل المثال:

public interface IMyInterface : IMyBaseInterface , IMyBaseInterface2 { // interface members }

كما في األصناف أيضا ويوفر ذلك آلية استخدام تعددية System.objectالصنف وجميع الواجهات ترث

األشكال مع الواجهات أيضا وكما نوهنا مسبقا فإنه من المستحيل إنشاء حاالت من الواجهات كما نقوم

بذلك في األصناف.

لنلق نظرة اآلن على بعض تعريفات األصناف في تطبيق بسيط.

Page 47: مستويات مبتدئ و متوسط -ج2

- 46 - إعداد: المهندس حسام الدين الرز

ف:تطبيق حول تعريف األصنا

.Console Definitions Classجديد باسم Consoleقم بإنشاء تطبيق -1

.Program.csأضف الشيفرة التالية إلى الملف -2

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Console_Definitions_Class { public abstract class MyBase { } internal class MyClass : MyBase { } public interface IMyBaseInterface { } internal interface IMyBaseInterface2 { } internal interface IMyInterface : IMyBaseInterface, IMyBaseInterface2 { } internal sealed class MyComplexClass : MyClass, IMyInterface { } class Program { static void Main(string[] args) { MyComplexClass myObj = new MyComplexClass(); Console.WriteLine(myObj.ToString()); Console.ReadLine(); } } }

.(2-1فيظهر الشكل ) F5نفذ الشيفرة بالضغط على زر -3

(2-1الشكل )

Page 48: مستويات مبتدئ و متوسط -ج2

- 47 - إعداد: المهندس حسام الدين الرز

كيفية العمل:

How to Work:

التالي حيث نبين UMLلقد قمنا في هذا المشروع بتعريف عدة واجهات واصناف وهي موضحة بتمثيل

هنا هرمية الوراثة لهذه العناصر:

(2-2الشكل )

تحوي على إطار يظهر العالقة بين الفئات والواجهات وهي Visual Studio.NET 2013إن إصدارة

من نافذة Program.csويمكننا إظهار مخطط مشروعنا هذا باختيار الملف UMLأشبه بمخطط

Solution Explorer اختيار البند األيمن و أرةومن ثم الضغط عليها بزر الفView Class Diagram

وتظهر ضمنها مخطط المشروع وفق تمثيل صندوقي كما في *ClassDiagram.cdفتظهر نافذة باسم

(.2-3الشكل )

Page 49: مستويات مبتدئ و متوسط -ج2

- 48 - إعداد: المهندس حسام الدين الرز

(2-3الشكل )

مالحظة:

من تنفيذ عدة أمور سوف نتعرف عليها في الفصل القادم لذا ال تقلق. *ClassDiagram.cdتمكننا

شكال السابقة جميع األصناف والواجهات التي يتضمنها مشروعنا الحظ أن الصنف تبين األ

System.object هو الصنف الذي ترث منه جميع العناصر في المشروع سواء األصناف أو حتى

فهو يمثل نقطة الدخول للتطبيق ككل. () Mainالواجهات وأما المنهج

هما عنصران عامان وبالتالي يمكننا الوصول IMyBaseInterfaceوالواجهة MyBaseإن الصنف

إليهما من خالل مشاريع أخرى أما األصناف والواجهات األخرى فهي داخلية لذا فإنه ال يمكننا الوصول

إليها من داخل مشروعنا الحالي فقط.

والذي يمثل كائنا من نوع myObjللكائن ()ToStringيقوم التطبيق السابق باستدعاء المنهج

MyComplexClass:

MyComplexClass myObj = new MyComplexClass(); Console.WriteLine(myObj.ToString());

Page 50: مستويات مبتدئ و متوسط -ج2

- 49 - إعداد: المهندس حسام الدين الرز

إال أننا قمنا باستخدام المنهج MyCompexClassوعلى الرغم من عدم وجود أية أعضاء ضمن الصنف

ToString() لكائن من نوع هذا الصنف وذلك ألن الصنفMyCompexClass ورث هذا المنهج من

)قيمة ومهمة هذا المنهج هي إعادة اسم صنف الكائن على شكل نص System.object الصنف األساس

(.Stringمن نوع

مالحظة:

مع أي صنف في System.objectإن هذا يعني أن بإمكاننا استخدام جميع خصائص ومناهج الصنف

C# .ويمكنك ان تجرب ذلك بنفسك

:System.objectن الكائ

System.object:

فإن جميع األصناف يمكنها الوصول إلى System.objectباعتبار أن جميع األصناف ترث من الصنف

األعضاء العامة والمحمية لهذا الصنف وبالتالي من المهم ان نستعرض تلك األعضاء يبين الجدول التالي

:System.objectالمناهج التي يتضمنها الصنف

منهجالالقيم

المعادة

منهج

ظاهري

منهج

ستاتيكي الوصف

Object() ال ال بدون

منهج بناء الكائنات من نوع

System.object ويتم استدعائه

بواسطة مناهج البناء لألنواع

المشتقة.

~object()

أو

Finalize()

ال ال بدون

منهج تدمير الكائنات من نوع

System.object ويتم استدعائه

ئيا بواسطة مناهج التدمير تلقا

لألنواع المشتقة.

Equals(object) bool ال نعم

objectيأخذ بارامتر من نوع

ويقوم بمقارنة الكائن الذي تم

استدعاء المنهج من خالله مع كائن

trueآخر )البارامتر( ويعيد القيمة

إن كان الكائنين متساويان ويمكن

تجاوز هذا المنهج إذا أردنا مقارنة

الكائنين بصورة مختلفة.

Equals(object ,

object) bool نعم ال

objectيأخذ بارامترين من نوع

ويقوم بمقارنة هذين الكائنين ويعيد

إن كان الكائنين trueالقيمة

Page 51: مستويات مبتدئ و متوسط -ج2

- 50 - إعداد: المهندس حسام الدين الرز

متساويان الحظ أنه إذا لم يكن

لهذين الكائنين وجود فإن التابع

.trueسيعيد القيمة

ReferenceEquals

(object , object) bool نعم ال

يقوم هذا المنهج بمقارنة كائنين

)البارامترين( ويتفحص فيما إذا

كان كال البارامترين يشيران إلى

الكائن نفسه.

ToString() String ال نعم

يعيد سلسلة نصية تمثل االسم

الكامل لصنف الكائن إال أنه يمكننا

تجاوز هذا المنهج وإعادة سلسلة

نصية أخرى.

MemberwiseClone() object ال نعم

يقوم بنسخ الكائن بإنشاء كائن جديد

ونسخ كافة األعضاء أيضا الحظ أن

عملية نسخ األعضاء ال تقوم

بإنشاء حاالت جديدة لتلك األعضاء

وبالتالي فإن أي أعضاء من نوع

مرجعي في الكائن الجديد ستشير

إلى الكائنات نفسها في الكائن

المنهج محمي األصلي إن هذا

وبالتالي يمكننا استخدامه ضمن

الصنف فقط أو ضمن أي صنف

يشتقه.

GetType() System.

Type ال ال

يعيد نوع الكائن على هيئة كائن من

System.Typeنوع

GetHashCode() int ال نعم

يستخدم كتابع فرز للكائنات التي

تتطلب ذلك حيث يعيد هذا التابع

هيئة مضغوطة.قيمة تعرف الكائن ب

وقد ال نستخدم أيا منها بتاتا NET.تلك هي المناهج األساسية التي تدعمها أنواع الكائنات في إطار عمل

.()GetHashCodeويمكن أن نستخدم بعضها في حاالت خاصة جدا مثل المنهج

حددة يام بعمليات ممفيد جدا عند استخدام تعددية االشكال باعتباره يسمح لنا بالق ()GetTypeإن المنهج

على الكائنات باالعتماد على أنواعها وذلك بدال من استخدام الشيفرة نفسها لجميع أنواع الكائنات التي قد

مما يعني أنه objectنواجهها في حالة ما على سبيل المثال إذا كان لدينا تابع يتقبل بارامتر من نوع

ورة أن نقوم بعمليات إضافية إذا تعرفنا على نوع الكائن بصيستطيع تمرير أي نوع من الكائنات فإنه يمكننا

يقوم بتحويل اسم الصنف #Cوهو عامل في ()typeofوالعامل ()GetTypeأدق وباستخدام المنهج

فإنه يمكننا القيام بعمليات مقارنة كما في الشيفرة: System.Typeالذي يتبع له الكائن إلى كائن من نوع

Page 52: مستويات مبتدئ و متوسط -ج2

- 51 - إعداد: المهندس حسام الدين الرز

if (typeof(myObj)==typeof (MyComplexClass)) { // myObj is an instance of the class MyComplexClass

}

المعاد يتضمن إمكانيات عديدة أكثر من هذه العملية إال أننا لن نتحدث عنه هنا System.Typeإن كائن

ر في فصول الحقة.وسوف نتحدث عن ذلك بتفصيل أكب

وذلك في الحاالت التي نود فيها تمثيل ()ToStringويمكننا ان نستفيد بصورة كبيرة من تجاوز المنهج

محتوى الكائن على هيئة سلسلة نصية بسهولة.

بصورة متكررة في الفصول القادمة لذا فإننا سنترك System.objectسوف نتعرف على مناهج الكائن

ومناهجه للحديث عنها عند الحاجة إلى ذلك. تفاصيل هذا الكائن

مناهج البناء والتدمير:

Constructors and Destructors:

فإننا نحتاج إلى تعريف منهج بناء وتدمير لهذا الصنف وعلى الرغم من أن #Cعندما نعرف صنفا في

أن هناك منهج يتضمن تزويدا افتراضيا لهذين المنهجين مما يعني System.Objectالصنف األساس

بصورة افتراضية إال أنه قد نحتاج في أوضاع معينة إلى إنشاء #Cتدمير ومنهج بناء ألي صنف في

مناهج بناء وتدمير مخصصة.

يأخذ منهج البناء نفس اسم الصنف ويمكننا ان ننشئ منهج بناء بصورة بسيطة كما يلي:

class MyClass { public MyClass () { // Constructor code }

}

وكما تالحظ فإن لمنهج البناء نفس اسم الصنف الذي يحتويه ونالحظ أن منهج البناء هنا ال يحتوي أية

بارامترات وهذا يعني أن هذا المنهج هو المنهج االفتراضي الذي ستنفذ شيفرته عند إنشاء كائن من هذا

مما يعني أنه يمكننا إنشاء حالة مع كائنات هذا publicاننا استخدمنا مقيد الوصول العام الصنف ونالحظ

الصنف عد للفصل السابق للمزيد من المعلومات حول هذا.

يمكننا أن نستخدم منهج بناء افتراضي خاص وهذا يعني أنه ال يمكننا إنشاء كائنات من هذا الصنف

ا يلي:باستخدام منهج البناء هذا وذلك كم

class MyClass { private MyClass () {

Page 53: مستويات مبتدئ و متوسط -ج2

- 52 - إعداد: المهندس حسام الدين الرز

// Constructor code }

}

ويمكننا أن نضيف منهج بناء غير افتراضي لصنفنا بنفس األسلوب ويتم التمييز بين منهج البناء االفتراضي

هذه المناهج فمنهج البناء االفتراضي ال يحتوي على ومناهج البناء غير االفتراضية من خالل تواقيع

مترات:ابار

class MyClass { public MyClass() { // Constructor code } public MyClass (int myInt) { // non - default Constructor code (uses myInt) }

}

وليس هناك أي حدود لعدد مناهج البناء التي يمكننا إنشائها في الصنف.

NET.تستخدم مناهج التدمير صيغة مختلفة قليلة عن صيغة تعريف مناهج البناء يسمى منهج التدمير في

ثل اسم المنهج الذي إال أن هذا ال يم ()Finalizeبالمنهج System.Objectوالذي يزود من قبل الصنف

سنستخدمه في تعريف مناهج تدمير ألصنافنا وإنما سنستخدم الصيغة التالية:

class MyClass { ~MyClass() { // destructor body }

ء من الموارد التي بتجميع النفايات مما يسمح بتحرير جز CLR يقوم شيفرة منهج التدمير عندماستنفذ

ة بصورة لمورثيحتلها الكائن وبعد استدعاء منهج التدمير سيتم استدعاء مناهج التدمير لألنواع األساس ا

الموجود في الصنف الجذر ()Finalizeخر باإلضافة إلى منهج التدمير ضمنية واحدة تلو اآل

System.Object الحظ انه يمكننا تجاوز المنهجFinalize() يمثل آخر منهج يقوم بتدمير الكائن والذي

من الذاكرة تماما وعند قيامنا بذلك فإن علينا القيام بعملية التدمير من الذاكرة بصورة يدوية أي بشكل

صريح وهو أمر خطير سوف نتعلم كيفية استدعاء مناهج الصنف األساس في الفصل القادم.

تسلسل تنفيذ مناهج البناء:

Constructor Execution Sequence:

إذا قمنا بعدة عمليات في مناهج البناء لصنف ما فإنه من المفيد ان نضع شيفرة هذه العمليات في مكان

ذلك سابقا ارأينواحد ولذلك نفس الفوائد التي يمكن أن نحصل عليها من تقسيم الشيفرة ضمن توابع كما

Page 54: مستويات مبتدئ و متوسط -ج2

- 53 - إعداد: المهندس حسام الدين الرز

توفر طريقة بديلة أفضل فيمكننا أن نعد #Cغة في هذا الكتاب يمكننا القيام بذلك باستخدام منهج ما إال أن ل

أي منهج بناء الستدعاء منهج بناء آخر قبل تنفيذ شيفرة منهج البناء نفسه.

وقبل أن نتناول كيفية ذلك فإن علينا أن نفهم ما يحصل عندما نقوم بإنشاء حالة من صنف ما.

حالة من الصنف األساس قبل ذلك لكي نتمكن من إنشاء حالة من صنف مشتق لصنف ما فإنه يجب إنشاء

ولكي نتمكن من إنشاء حالة من الصنف األساس هذا فإن علينا إنشاء حالة من صنفه األساس أيضا وهكذا

ومحصلة ذلك بغض النظر عن منهج System.Objectإلى أن نصل إلنشاء حالة من الصنف الجذر

سينفذ أوال. System.Objectفي الصنف البناء المستخدم إلنشاء حالة من صنف ما فإن منهج البناء

إذا استخدمنا منهج بناء غير افتراضي لصنف ما فإن السلوك االفتراضي هو استخدام منهج بناء الصنف

األساس الذي يطابق توقيعه منهج بناء الصنف المشتق فإن لم يكن هناك منهج بناء مطابق عندها سيستدعى

وهو األمر الذي يحدث دائما عند الوصول إلى الصنف األساس منهج البناء االفتراضي للصنف األساس

System.Object .وذلك ألن ليس لهذا الصنف مناهج بناء غير منهج البناء االفتراضي

لنأخذ مثاال سريعا يستعرض لنا تسلسل تنفيذ األحداث لتكن لدينا هرمية األصناف التالية:

public class MyBaseClass { public MyBaseClass () { } public MyBaseClass (int i) { } } public class MyDerivedClass:MyBaseClass { public MyDerivedClass () { } public MyDerivedClass (int i) { } public MyDerivedClass (int i,int j) { }

}

بالصورة التالية: MyDerivedClassإذا قمنا بإنشاء حالة من الصنف

();MyDerivedClass newmyObj = MyDerivedClass

فإن تسلسل ما سيحدث عند تنفيذ هذا السطر هو بالشكل التالي:

تنفيذ منهج البناء System.Object.Object().

ة تنفيذ منهج البناءMyBaseClass.MyBaseClass().

Page 55: مستويات مبتدئ و متوسط -ج2

- 54 - إعداد: المهندس حسام الدين الرز

تنفيذ منهج البناء MyDerivedClass.MyDerivedClass().

كما يلي: MyDerviedClassوبصورة بديلة إذا أنشأنا حالة من الصنف

(4);MyDerivedClass newmyObj = MyDerivedClass

فإن تسلسل التنفيذ سيأخذ الشكل اآلتي:

تنفيذ منهج البناء System.Object.Object().

تنفيذ منهج البناءةMyBaseClass.MyBaseClass(int i).

تنفيذ منهج البناءMyDerivedClass.MyDerivedClass(int i).

وأخيرا إذا أنشأنا حالة من الصنف نفسه بالشكل:

(4 , 8);MyDerivedClass newmyObj = MyDerivedClass

هو بالشكل التالي: فإن تسلسل ما سيحدث عند تنفيذ هذا السطر

تنفيذ منهج البناء System.Object.Object().

تنفيذ منهج البناءةMyBaseClass.MyBaseClass().

تنفيذ منهج البناءMyDerivedClass.MyDerivedClass(int i , int j).

ء في إن هذا النظام يعمل بشكل جيد ويتأكد من أن أي أعضاء موروثة يمكن أن تصل إلى منهاج البنا

األصناف المشتقة إال أن هناك في بعض األحيان حاالت نحتاج فيها لمزيد من التحكم في مناهج البناء

المستدعاة على سبيل المثال قد نرغب في المثال األخير بتنفيذ تسلسل يأخذ الصورة التالية:

تنفيذ منهج البناء System.Object.Object().

تنفيذ منهج البناءةMyBaseClass.MyBaseClass(int i).

تنفيذ منهج البناءMyDerivedClass.MyDerivedClass(int i , int j).

وهذا MyBaseClass(int i)في منهج البناء int i البارامترسيؤدي هذا النوع من التسلسل إلى استخدام

يحتاج في عمل أقل سيقوم به ولن MyDerivedClass(int i , int j)يعني أنه سيكون لمنهج البناء

مهمة معالجة MyBaseClass(int i)حيث سيتولى منهج البناء jحالة كهذه إال لمعالجة البارامتر

من #Cوتمكننا لغة MyDerivedClass(int i , int j)المذكور في بارامتر المنهج int iالبارامتر

اتباع هذا النوع من السلوك إذا شئت.

مالحظة:

له MyDerivedClass(int i , int j) في منهج البناء int iلبارامتر لقد افترضنا هنا أن يكون ا

وهذا األمر ليس صحيحا دائما MyBaseClass(int i)في منهج البناء int iالمعنى نفسه للبارامتر

إال أن األمر سيكون بهذه الصورة في أغلب األحيان عندما نصادف حالة كهذه.

Page 56: مستويات مبتدئ و متوسط -ج2

- 55 - إعداد: المهندس حسام الدين الرز

ك ضمن منهج البناء للصنف المشتق وحسب المثال هذا فإننا ستعدل وللقيام بذلك فإن علينا ان نحدد ذل

كما يلي: MyDerivedClass(int i , int j)توقيع منهج البناء

public class MyDerivedClass:MyBaseClass { public MyDerivedClass () { } public MyDerivedClass (int i) { } public MyDerivedClass (int i,int j) : base(i) { }

}

هنا إلى أنه سيتم استخدام منهج بناء الصنف األساس الموافق للتوقيع المحدد baseتشير الكلمة المفتاحية

سيستخدم MyBaseClass(int i)ج البناء واحد وبالتالي فإن منه intلقد استخدمنا هنا بارامتر من نوع

لن يستدعى وبالتالي ستأخذ ()MyBaseClassعندها إن القيام بذلك يعني أن منهج البناء االفتراضي

التسلسل األخير الذي أوردناه مسبقا وهو ما أردنا MyDerivedClassعملية إنشاء حالة من الصنف

الحصول عليه تماما.

لتحديد قيم حرفية مباشرة لمناهج بناء الصنف األساس فلربما كنا نود استخدام base يمكننا استخدام الكلمة

الستدعاء منهج البناء غير االفتراضي للصنف MyDerivedClassمنهج البناء االفتراضي

MyBaseClass:

public MyDerivedClass (int i,int j) : base(5) { }

تسلسل التنفيذ التالي:وهذا سيعطينا

تنفيذ منهج البناء System.Object.Object().

تنفيذ منهج البناءةMyBaseClass.MyBaseClass(5).

تنفيذ منهج البناءMyDerivedClass.MyDerivedClass(int i , int j).

تقوم هذه thisفإن هناك كلمة مفتاحية أخرى يمكننا استخدامها هنا وهي baseوباإلضافة إلى الكلمة

الكلمة باستخدام منهج البناء غير االفتراضي للصنف الحالي قبل استدعاء منهج البناء الحالي وذلك كما

يلي:

public class MyDerivedClass:MyBaseClass { public MyDerivedClass () : this(4,5) { } public MyDerivedClass (int i) {

Page 57: مستويات مبتدئ و متوسط -ج2

- 56 - إعداد: المهندس حسام الدين الرز

} public MyDerivedClass (int i,int j) : base(i) { }

}

لإلشارة إلى الصنف األساس baseلإلشارة إلى الصنف الحالي بينما تستخدم الكلمة thisتستخدم الكلمة

ل التالي:المورث وبناء على ذلك فإن تسلسل التنفيذ هنا سيأخذ الشك

تنفيذ منهج البناء System.Object.Object().

تنفيذ منهج البناءةMyBaseClass.MyBaseClass(int i).

تنفيذ منهج البناءMyDerivedClass.MyDerivedClass(int i , int j).

تنفيذ منهج البناءMyDerivedClass.MyDerivedClass().

base يمكننا تحديد إال منهج بناء واحد فقط باستخدام الكلمتين والمحدودية الوحيدة في كل ذلك هو أنه ال

لكن وكما يظهر لنا في المثال السابق فإن تلك ال تعد بمشكلة في حد ذاتها باعتبار أنه ما يزال thisأو

بإمكاننا استخدام أشكال مختلفة من تسلسل تنفيذ مناهج البناء.

:Visual Studio.NETفي OOPأدوات

OOP Tools in Visual Studio.NET:

فإن هناك العديد من األدوات NET.بما أن البرمجة كائنية التوجه تعد موضوعا جوهريا في إطار عمل

وسوف نتناول في هذا القسم بعضا منها. OOPوالموجهة لتطوير تطبيقات VSالتي يوفرها

:Class Viewإطار عرض الصنف

The Class View Window:

في الفصل الثاني من الجزء األول ورأينا Solution Explorerعلى إطار مستعرض الحل لقد تعرفنا

يستعرض هذا Class Viewأن هناك إطار آخر يتشارك مع هذا اإلطار في نفس النافذة ويسمى بإطار

اإلطار شجرة أصناف تطبيقاتنا ويمكننا من رؤية مواصفات هذه األصناف التي نستخدمها وأعضائها

Sort Byهناك عدد من أنماط العرض المختلفة لهذه المعلومات والنمط االفتراضي هو أيضا

Alphabetically فبالنسبة لشجرة المثال في القسم السابق سيعرض حروف الهجاءأي الترتيب حسب

بالشكل التالي: Console Definitions Classهذا اإلطار شجرة الصنف الرئيسي

Page 58: مستويات مبتدئ و متوسط -ج2

- 57 - إعداد: المهندس حسام الدين الرز

(2-4الشكل )

من األيقونات التي نالحظها ضمن اإلطار ولكل شكل من أشكال هذه االيقونات مدلول خاص هناك عدد

بها وهي كما يلي:

الوصف )اإلنكليزي( الوصف )العربي( شكل االيقونة

Project مشروع

Namespace فضاء أسماء

Interface واجهة

Class صنف

Method منهاج

Property خاصية

Object كائن

Access الوصولية مقيد

Data Type بياناتالنوع

Enumeration تعدادات

Enumeration Item عنصر من تعداد

Event حدث

Structالحظ استخدام بعض هذه األيقونات لوصف أنواع أخرى غير األصناف مثل التعدادات وأنواع

هناك بعض العناصر التي قد يكون لها رموز أخرى متوضعة في األسفل وتشير إلى مستوى الوصول

:publicالعناصر حيث لن يظهر أي رمز للعناصر العامة إلى هذه

Page 59: مستويات مبتدئ و متوسط -ج2

- 58 - إعداد: المهندس حسام الدين الرز

الوصف )اإلنكليزي( الوصف )العربي( شكل االيقونة

Private خاص

Internal داخلي

Protected محمي

abstractأو حتى المجردة Sealedنغلقة أو الم Virtualوليس هناك أية رموز تحدد العناصر الظاهرية

إن جميع أنماط العرض المتوفرة في هذا اإلطار تستخدم بنفس األسلوب وتمكننا من توسعة تعاريف

األصناف باستخدام عناصر تحكم عرض شجرية إن توسعة صنفنا والواجهات وصوال إلى الصنف

System.Object سيقودنا إلى إطارClass View (:2-5ل )كما في الشك

(2-5الشكل )

ويمكننا ان نرى جميع معلومات األصناف والواجهات في المشروع وباإلضافة إلى إمكانية استعراض

معلومات األصناف والواجهات وأعضائها فبإمكاننا الوصول إلى الشيفرة الموافقة لهذه العناصر وهي

Page 60: مستويات مبتدئ و متوسط -ج2

- 59 - إعداد: المهندس حسام الدين الرز

ر أو بالضغط بزر الفأرة األيمن ومن عادة سطر تعريف هذه العناصر بمجرد النقر المزدوج على العنص

من القائمة المنسدلة إن هذا سيحصل إذا كان هناك فعال تعريف لهذا Go to Definitionثم اختيار

العنصر ضمن الشيفرة البرمجية للحل أو المشروع المفتوح حاليا وإن لم يكن هناك تعريف لهذا العنصر

Objectفسننتقل إلى إطار جديد باسم System.Objectكالشيفرة الموجودة ضمن النوع األساس

Browser.

مستعرض الكائنات:

The Object Browser:

حيث يوفر مستعرض الكائنات Class Viewإصدارة موسعة إلطار Object Browser تمثل نافذة

إمكانية استعراض األصناف األخرى الموجودة في مشروعنا إضافة إلى إمكانية عرض أصناف خارجية

في Viewمن القائمة Object Browserيضا يمكننا الوصول إلى مستعرض الكائنات باختيار األمر أ

Object Browserيبين الشكل التالي نافذة Visual Studio.NET 2013شريط القوائم المنسدلة لـ

في نفس Class Viewوهي تظهر ضمن اإلطار الرئيسي وبالتالي يمكننا عرض هذه النافذة وإطار

الوقت:

(2-6الشكل )

الذي يستعرض Class Viewتبين لنا هذه النافذة األصناف وأعضاءها في منطقتين بعكس إطار

المستخدمة NET.مكان واحد هذا باإلضافة إلى أنه يمكننا الوصول إلى وحدات وأعضاءها فياألصناف

حتوى لتالي يمكننا استعراض مفي تطبيقنا أيضا وليس مجرد األصناف الموجودة في مشروعنا وحسب وبا

مثال باستخدام هذه النافذة. Systemفضاء األسماء

Page 61: مستويات مبتدئ و متوسط -ج2

- 60 - إعداد: المهندس حسام الدين الرز

عند اختيار عنصر من إحدى القائمتين هنا سيتم استعراض معلومات عن العنصر الذي تم اختياره في

صنف لالنافذة ويمكننا أن نجد في هذا الشكل المعلومات التي تستعرضها هذه النافذة عند اختيار ا يمين أسفل

Program من تطبيقناConsole Definitions Class تلخيصيه عن ويمكننا أن نجد أيضا معلومات

في الحقيقة يتم توليد هذه المعلومات من خالل تعليقات مكتوبة Summaryالعنصر مبوبة تحت العنوان

أكبر بتفصيل XMLه األصناف سوف نتحدث عن تعليقات ضمن الشيفرة المصدرية لهذ XMLبهيئة

في الفصول القادمة.

إضافة األصناف:

Adding Classes:

مجموعة أدوات تسهل تأدية المهام الشائعة وبعض هذه المهام Visual Studio.NET 2013يتضمن

وأحد هذه األدوات يمكننا من إنشاء أصناف جديدة في تطبيقنا بسرعة. OOPمالئمة لالستخدام مع تقنيات

أو باختيار األمر PROJECTمن القائمة Add Classألداة باختيار األمر يمكننا الوصول إلى هذه ا

Add New Item من نفس القائمة أيضا أو بالنقر بزر الفارة األيمن على مشروعنا في إطارSolution

Explorer واختيار األمرNew Item اوClass الفرعية لألمر من القائمةAdd من القائمة المنسدلة

( 2-7ئذ صندوق حوار كما في الشكل )سيظهر عند

(2-7الشكل )

Page 62: مستويات مبتدئ و متوسط -ج2

- 61 - إعداد: المهندس حسام الدين الرز

وليكن ويمكنك تحديد اسم الصنف في مربع النص في األسفل Classيمكنك عندئذ تحديد البند

MyNewClass وعند االنتهاء اضغط على زرAdd سيتم بواسطته إنشاء صنف سيأخذ نفس االسم

الذي حددته في صندوق الحوار هذا.

بصورة يدوية Program.csلمثال السابق من هذا الفصل تعريفات األصناف إلى الملف لقد أضفنا في ا

إن وضع األصناف في ملفات منفصلة يساعدنا في الغالب على تسهيل التعامل معها.

يحتوي على MyNewClass.csباسم إن ما قمنا به في صندوق الحوار السابق سيؤدي إلى فتح ملف

الشيفرة التالية:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Console_Definitions_Class { class MyNewClass { } }

Console Definitionsالحظ أنه قد تم تعريف هذا الصنف ضمن فضاء األسماء للمشروع نفسه أي

Class والذي تم تعريف الصنفProgram الذي يمثل صنف نقطة الدخول للمشروع ضمنه أيضا

تماما كما لو كان MyNewClassوبالتالي يمكننا أن نستخدم هذا الصنف من ضمن شيفرة صنفنا

الصنفان ضمن الملف نفسه.

توضيح:

تطبيق.بصنف نقطة الدخول لل Consoleفي تطبيق ()Mainنسمي الصنف الذي يحتوي على التابع

مشاريع مكتبات األصناف:

Class Library Projects:

وكما يمكننا أن نضع األصناف في ملفات منفصلة ضمن تطبيقنا فإنه يمكننا أن نضعها ضمن مشاريع

تمنفصلة تماما يسمى المشروع الذي ال يتضمن إال عددا من األصناف باإلضافة إلى بعض أنواع التعريفا

.Class Libraryالمرتبطة األخرى وال يحتوي على نقطة دخول بمكتبة األصناف

ويمكننا االستفادة من dll.تتم ترجمة مشاريع مكتبات األصناف إلى مجمعات )ملفات( لها االمتداد

بإضافة مراجع لها في المشاريع التي ستستخدمها والتي يمكن أن تمثل assembliesمحتويات المجمعات

ا من الحل نفسه إال أنه ليس بالضرورة أن يكون ذلك.جزء

Page 63: مستويات مبتدئ و متوسط -ج2

- 62 - إعداد: المهندس حسام الدين الرز

فكرة:

إن وضع األصناف ضمن المجمعات بهذه الصورة يمثل إحدى التقنيات الرائعة المستخدمة إلعادة

استخدام الشيفرة المصدرية في أكثر من تطبيق فيمكنك مثال أن تبرمج عددا من األصناف التي تقوم

ر والوسطى للمصفوفات وتضعها ضمن صنف واحد ومن ثم تقوم ببرمجة باحتساب القيمة األكبر واألصغ

عدد أخر من األصناف المختصة بالعمليات الحسابية أيضا عندئذ يمكنك أن تضع هذه األصناف جميعها

NET.وتستخدمها في أي تطبيق لك في Math.dllضمن مجمعة باسم

خدم ء مكتبة أصناف ومشروع آخر منفصل يستلنلق اآلن نظرة على مثال بسيط نستعرض فيه كيفية إنشا

األصناف الموجودة في هذه المكتبة.

تطبيق حول استخدام مكتبة األصناف:

.Class Library Projectsوليكن اسمه Class Libraryقم بإنشاء مشروع جديد من النوع -1

(2-8الشكل )

القيام بذلك بالنقر بزر يمكنك MyExternalClass.csإلى االسم Class1.csغير اسم الملف -2

من القائمة Renameواختيار Solution Explorerالفأرة األيمن على الملف في اإلطار

المنسدلة.

لكي يعكس التغيير الذي أحدثناه في اسم MyExternalClass.csعدل الشيفرة في الملف -3

الصنف:

Page 64: مستويات مبتدئ و متوسط -ج2

- 63 - إعداد: المهندس حسام الدين الرز

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Class_Library_Projects { public class MyExternalClass { public MyExternalClass() { } } }

.MyInternalClass.csقم بإضافة صنف جديد إلى المشروع وليكن له االسم -4

عدل شيفرة الصنف الجديد وذلك لجعله صنفا داخليا: -5

using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Class_Library_Projects { internal class MyInternalClass { public MyInternalClass () { } } }

ترجم المشروع )الحظ انه ليس لهذا المشروع نقطة دخول وبالتالي لن تستطيع تنفيذ هذا المشروع -6

بالطريقة العادية كما كنا نقوم بذلك في السابق وبدال من ذلك فإننا سنقوم ببناء المشروع باختيار

(.BUILDمن القائمة Solution Buildاألمر

Consoleواختر تطبيق Projectثم اختر االمر الفرعي Newاختر األمر Fileلقائمة من ا -7

.Console Class Library Projectsغير اسم التطبيق باسم

من القائمة Referenceأو اختر االمر PROJECTمن القائمة Add Referenceاختر االمر -8

Add بعد النقر بزر الفارة األيمن على إطارSolution Explorer .من القائمة المنسدلة

Page 65: مستويات مبتدئ و متوسط -ج2

- 64 - إعداد: المهندس حسام الدين الرز

(2-9الشكل )

Class Libraryثم انتقل إلى المجلد الذي حفظت ضمنه مشروع Browseانقر على الزر -9

Projects ومن ثم انتقل إلى المجلدbin ثم إلى المجلدDebug ثم قم باختيار الملف Class

Library Projects.dll ثم اضغط على الزرAdd ثم الزرok:

(2-10الشكل )

Page 66: مستويات مبتدئ و متوسط -ج2

- 65 - إعداد: المهندس حسام الدين الرز

.Solution Explorerعند انتهاء العملية تأكد من ان هناك مرجعا قد تمت إضافته في إطار -10

(2-11الشكل )

وتفحص المرجع الجديد لرؤية األصناف Object Browserافتح نافذة مستعرض الكائنات -11

واألعضاء التي يتضمنها.

(2-12الشكل )

Page 67: مستويات مبتدئ و متوسط -ج2

- 66 - إعداد: المهندس حسام الدين الرز

كما يلي: Program.csعدل شيفرة الملف -12

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Class_Library_Projects; namespace Console_Class_Library_Projects { class Program { static void Main(string[] args) { MyExternalClass myObj = new MyExternalClass(); Console.WriteLine(myObj); Console.ReadLine (); } } }

كما يلي: Consoleنفذ التطبيق وستظهر نافذة -13

(2-13الشكل )

كيفية العمل:

How to Work:

في هذا المثال بإنشاء تطبيقين يمثل التطبيق األول مشروعا لمكتبة أصناف واألخر يمثل تطبيق لقد قمنا

Console يتضمن مشروع مكتبة األصنافClass Library Projects صنفينMyExternalClass

والذي له وصولية داخلية أما التطبيق الثاني MyInternalClassوالذي له وصولية عامة والصنف

Console Class Library Projects فهو تطبيقConsole والذي يتضمن شيفرة بسيطة هدفها

استخدام مكتبة األصناف تلك.

فإن علينا ان Class Library Projectsولكي نتمكن من استخدام األصناف الموجودة في المشروع

Console Class Library Projectsنضيف مرجعا لها في التطبيق الذي يود استخدامها وهو التطبيق

المولد عند بناء التطبيق Class Library Projects.dllفي حالتنا هنا أي أننا سنضيف مرجعا للملف

وعلى الرغم Consoleولتبسيط األمور في هذا المثال فإننا اكتفينا بمجرد إضافة مرجع لهذا الملف تطبيق

Page 68: مستويات مبتدئ و متوسط -ج2

- 67 - إعداد: المهندس حسام الدين الرز

Console Classإلى نفس مجلد التطبيق Class Library Projects.dllمن أنه يمكننا أن ننسخ الملف

Library Projects .إال أننا نود االستمرارية في تطوير مكتبة التطبيقات تلك وهي في مكانها

توضيح:

لهذه المكتبة عناء كبيرا يكفي أن ننسخ اإلصدارة الحديثة من الملف dllتتطلب عملية تحديث ملف

ال #Cيدة مباشرة! آه إن هذا يعني أن عملية تثبيت تطبيقات فوق اإلصدارة القديمة وتستخدم الجد

تتطلب سوى نسخ الملفات إلى المكان الذي نود تنفيذها منه فعال إن الحياة تصبح أبسط بكثير مع إطار

NET.عمل

كتبة األصناف تلك فإننا قمنا باستكشاف األصناف المتوفرة ضمنها من خالل موبعد أن أضفنا مرجعا ل

مع MyExternalClassلكن الحظ أنه ال وجود إال لصنف واحد فقط هو Object Browserنافذة

العلم ان مكتبة األصناف تحتوي على صنفين إذن أين الصنف الثاني؟

MyInternalClassوهو الصنف internalال تنسى أننا عرفنا الصنف الثاني على أنه صنف داخلي

وهذا يعني أن Object Browser ة مستعرض الكائنات ن هذا الصنف لن يعرض ضمن نافذولذلك فإ

الذي Class Library Projectsهو الصنف الوحيد في مكتبة األصناف MyExternalClassالصنف

هذا. Consoleاألخرى أي من خالل شيفرة خارجية مثل تطبيق تطبيقاتنايمكننا الوصول إليه من خالل

بالشيفرة التالية والتي تحاول استخدام Consoleفي تطبيق ()Mainيمكننا أن نستبدل شيفرة التابع

الصنف الداخلي:

MyInternalClass myObj = new MyInternalClass(); Console.WriteLine(myObj);

.ReadLine ();Console

إال ان عملية كهذه ستؤدي إلى رسالة خطأ من المترجم.

ألة استخدام األصناف الموجودة في المجمعات من خالل مجمعات أخرى تمثل المفتاح للبرمجة عن مس

وفي الحقيقة إن هذا ما نقوم به عندما نستخدم األصناف المعرفة في مكتبة NET.وإطار عمل #Cفي

NET.أصناف إطار عمل

توضيح:

ة من المجمعات وأشياء هو مجموعة من مكتبات األصناف أي مجموع NET.الحظ أن إطار عمل

أخرى تمت كتابتها مسبقا بحيث تسهل الكثير من المهام البرمجية علينا.

الواجهات مقابل األصناف المجردة:

Interfaces vs. Abstract Classes:

Page 69: مستويات مبتدئ و متوسط -ج2

- 68 - إعداد: المهندس حسام الدين الرز

دث عن ننا لن نتحهات واألصناف المجردة كما قلنا فإفي هذا الفصل كيف يمكننا ان ننشئ الواج رأينالقد

ة نقاط ومن ان في عدي هذا الفصل وربما قد الحظت ان الصنف المجرد والواجهة متشابهاألعضاء ف

الجدير بالذكر بنا أن نتناول جوانب كال منها وأن نبين متى يتوجب علينا استخدام إحداهما بدال من األخرى

أوال بأوجه الشبه. ولنبدأ

توريثها لصنف أخر وال يمكن أن ننشئ يمكن أن تحتوي األصناف المجردة والواجهات على أعضاء يمكن

حالة من الواجهات وال حتى األصناف المجردة بصورة مباشرة وإنما يمكننا ذلك بواسطة التصريح عن

متحوالت من هذه األنواع أي من نوع الواجهة أو الصنف المجرد وفي كلتا الحالتين فإننا لن نتمكن من

ك المتحوالت على الرغم من أننا ال نستطيع الوصول المباشر استخدام أعضاء هذه األنواع إال من خالل تل

على األعضاء األخرى للكائن المشتق.

لنتحدث اآلن عن االختالفات.

يمكن لألصناف أن ترث من صنف أساس وحيد وهذا يعني انه ال يمكن للصنف الواحد أن يرث إال صنفا

صنف بأي عدد من الواجهات إال أن هذا ال مجردا واحدا فقط بصورة مباشرة وبالعكس يمكن أن يزود ال

يوضح أي فرق جوهري بينهما حتى اآلن.

توضيح:

يمكن للصنف أن يرث أكثر من صنف مجرد واحد لكن ذلك يحدث بصورة غير مباشرة نتيجة للوراثة

صنفا مجردا ومن ثم يرث Aمن األصناف ذات المستويات األعلى في هرمية الوراثة كأن يرث الصنف

.Aخر الصنف صنف آ

يمكن أن يحتوي الصنف المجرد على أعضاء مجردة وغير مجردة أيضا أما أعضاء الواجهة فيجب أن

يتوفر لها جميعا تزويد ضمن الصنف المجرد بهذه الواجهة فالواجهة ال تحتوي على أية كتل برمجية وإنما

سب تعريف الواجهة مجرد تعريفات لألعضاء فقط وكذلك فإن أعضاء الواجهة هي أعضاء عامة ح

باعتبار أن وجودها لالستخدام الخارجي فقط إال أن أعضاء األصناف المجردة يمكن أن تكون خاصة

طالما أنها ليست أعضاء مجردة أو محمية أو داخلية محمية وباإلضافة إلى ذلك فإن الواجهات ال يمكن

و حتى متحوالت ثابتة.أن تحتوي على حقول أو مناهج بناء أو تدمير أو أعضاء ستاتيكية أ

مالحظة:

نعيد ونذكر بأن ما نقصده بتزويد جميع أعضاء الواجهة ضمن الصنف المزود بهذه الواجهة يعني كتابة

شيفرة لهذه األعضاء ضمن الصنف تحدد سلوك هذه األعضاء.

مالحظة:

أن تتضمن األصناف أعضاء مجردة وغير مجردة.يجب أن تنتبه إلى أنه من الممكن أيضا

Page 70: مستويات مبتدئ و متوسط -ج2

- 69 - إعداد: المهندس حسام الدين الرز

توضيح:

األعضاء المجردة هي تلك األعضاء التي ال تحتوي على شيفرة وإنما على مجرد تعريف للعضو فقط.

وبالتالي يجب أن تتضمن األصناف التي ترث هذه األصناف تزويدا لهذه األعضاء ضمنها إال إذا كان

الصنف المشتق هو أيضا صنف مجرد.

Virtualعلى كتلة شيفرة ويمكن أن تكون ظاهرية أما األعضاء غير المجردة فهي أعضاء تحتوي

وبالتالي يمكن تجاوزها في الصنف المشتق.

وخالصة ذلك أن هذين النوعين يستخدمان ألغراض مختلفة فالصنف المجرد يستخدم أساس للكائنات

التي تشترك مع بعضها البعض بصفات مشتركة أما الواجهة فهي مصممة لكي تستخدم من قبل األصناف

لتي يمكن ان تختلف عن بعضها في مستويات أساسية إال أن هذه األصناف تقوم ببعض العمل نفسه.ا

وكمثال على ذلك لنفترض أن لدينا عائلة من الكائنات تمثل مجموعة من القطارات يتضمن الصنف

للقطارات على التعريف األساسي للقطار مثل عرض السكة الحديدية ونوع المحرك Trainاألساس

الذي يمكن أن يكون محركا بخاريا أو كهربائيا وذلك يمثل صنفا مجردا باعتبار أنه ليس هناك ما يعرف و

بالقطار العام ولكي نتمكن في الحقيقة من إنشاء قطار حقيقي علينا أن نضيف خصائص محددة لهذا القطار

أو صنف قطار PassengerTrainأن نشتق أصنافا مثل صنف قطار الركاب وللقيام بذلك فإن علينا

.DoubleBogey424أو قطار المناجم FreightTrainالشحن

(2-14الشكل )

ويمكن Carويمكننا أن نعرف عائلة كائنات السيارات بنفس األسلوب أيضا وذلك باستخدام صنف مجرد

.PickUPأو SUVأو Compactأن نكون أصنافا مشتقة من هذا الصنف مثل

المجردين من صنف مجرد أب وليكن Trainوالقطارات Carصنفي السيارات ويمكننا أيضا أن نشتق

:Vehicleصنف العربات

Page 71: مستويات مبتدئ و متوسط -ج2

- 70 - إعداد: المهندس حسام الدين الرز

(2-15الشكل )

واآلن يمكن ان تتشارك بعض األصناف اإلضافية في أسفل الهرمية ببعض الخصائص بسبب أغراضها

اف كن لألصنوليس تشاركها فقط لمجرد أنها تشتق من الصنف األساس نفسه على سبيل المثال يم

PassengerTrain وCompact وSUV وPickup ان تتمكن جميعها من نقل الركاب وبالتالي فإنه

ويمكننا أيضا أن نزود IPassengerCarrierيمكننا أن نزود جميع هذه األصناف بواجهة نقل الركاب

.IHeavyLoadCarrierبواجهة نقل البضائع Pickupو FreightTrainالصنفين

(2-16)الشكل

باستخدامنا لنظام مجزأ بواسطة الكائنات بهذه الصورة قبل القيام بإسناد أية قيمة معينة لها يمكننا معرفة

األوضاع التي تحدد لنا متى نستخدم األصناف المجردة ومتى نستخدم الواجهات إن هذا المثال الذي

الواجهات فقط.طرحناه هنا ال يمكننا تحقيقه باستخدام األصناف المجردة فقط أو

Page 72: مستويات مبتدئ و متوسط -ج2

- 71 - إعداد: المهندس حسام الدين الرز

:Structأنواع البنى

Struct Types:

واألصناف متشابهان بصورة كبيرة إال أن أنواع Structلقد نوهنا في الفصل السابق من أن أنواع البنى

ن حسنا إ هي أنواع قيمة أما األصناف فهي أنواع مرجعية لكن ماذا يعني هذا بالنسبة إلينا؟ Structالبنى

ذلك هو بضرب مثال: أبسط طريقة لتوضيح

:Structمثال حول األصناف مقابل بنى

.Console Struct Typesجديد باسم Consoleقم بإنشاء تطبيق -1

عدل الشيفرة كما يلي: -2

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Console_Struct_Types { class MyClass { public int val; } struct MyStruct { public int val; } class Program { static void Main(string[] args) { MyClass ObiectA = new MyClass(); MyClass ObjectB = ObiectA; ObiectA.val = 10; ObjectB.val = 20; MyStruct StructA = new MyStruct(); MyStruct StructB = StructA; StructA.val = 30; StructB.val = 40; Console.WriteLine("ObjectA.val={0}", ObiectA.val); Console.WriteLine("ObjectB.val={0}", ObjectB.val); Console.WriteLine("StructA.val={0}", StructA.val); Console.WriteLine("StructB.val={0}", StructB.val); Console.ReadLine(); } } }

Page 73: مستويات مبتدئ و متوسط -ج2

- 72 - إعداد: المهندس حسام الدين الرز

.F5نفذ التطبيق بالضغط على زر -3

(2-17الشكل )

كيفية العمل:

How to Work:

وهو يحتوي على حقل واحد من MyStructباسم Structيتضمن هذا التطبيق تعريفين أحدهما للنوع

ويحتوي على حقل مشابه للحقل المعرف MyClassواألخر هو صنف يسمى بـ valباسم intنوع

.MyStructضمن البنية

وباإلضافة إلى تعريف هذين النوعين فإن الشيفرة السابقة تقوم بالعمليات التالية لكل نوع من هذه األنواع:

.التصريح عن متحول النوع

.)إنشاء حالة جديدة من النوع في هذا المتحول )أي إنشاء كائن من النوع

.التصريح عن متحول آخر من النوع

.إسناد المتحول األول إلى الثاني

إسناد قيمة للحقلval .في الكائن األول

إسناد قيمة للحقلval .في الكائن الثاني

عرض قيمةval .)في كال الكائنين)المتحولين

وعلى الرغم من أننا نقوم بالعمليات نفسها على متحوالت لكال النوعين إال أن النتائج ليست نفسها.

هذا الحقل هي نفسها في كال المتحولين من نوع فإننا نجد أن قيمة valفعندما نعرض قيمة الحقل

MyClass بينما تكون قيمة الحقلval مختلفة في المتحولين من نوعMyStruct.

يحدث؟إذا ماذا

لقد قلنا مسبقا أن الكائنات هي أنواع مرجعية وبالتالي عندما نقوم بإسناد كائن إلى متحول فإننا نقوم بإسناد

إلى الكائن والمؤشر عبارة عن عنوان في الذاكرة وبناء على ذلك فإن pointerهذا المتحول مؤشرا

العنوان هو موضع في الذاكرة يتواجد فيه الكائن فيها وعندما قمنا بإسناد مرجع الكائن األول إلى متحول

فإن ما قمنا به في الحقيقة هو نسخ هذا العنوان وهذا يعني أن كال المتحولين MyClassآخر من نوع

ويان على مؤشر للكائن نفسه.يحت

Page 74: مستويات مبتدئ و متوسط -ج2

- 73 - إعداد: المهندس حسام الدين الرز

واع قيمة وهذا يعني أنه بدال من وضع مؤشر للبنية ضمن المتحول فإن هذا نفهي أ Structاما أنواع

المتحول سيحتوي على البنية نفسها وعندما نقوم بإسناد البنية األولى على المتحول الثاني من نوع

MyStruct أخرى إن هذا السلوك ىلمعلومات من بنية إلفإن ما نقوم به في الحقيقة هو نسخ جميع ا

مطابق تماما لعمليات اإلسناد الطبيعية التي قمنا بها على مدار الفصول السابقة من إسناد قيم من أنواع

إلى متحوالت من نفس األنواع والنتيجة النهائية هي ان متحولي البنيتين يحتويان على intبسيطة مثل

بنيتين مختلفتين عن بعضهما.

يجعل كتابة الشيفرة أسهل وأبسط #Cإن استخدام المؤشرات بهذه الصورة غير الواضحة بالنسبة لمبرمج

باستخدام ما يعرف #Cومن الممكن الوصول إلى عمليات منخفضة المستوى مثل معالجة المؤشرات في

ه في كتابنا هذا.إال أن هذا الموضوع هو موضوع متقدم ولن نتناول unsafe codeبالشيفرة غير اآلمنة

النسخ السطحي والنسخ العميق:

Shallow vs. Deep Copying:

إن مسألة نسخ الكائنات من متحول إلى آخر بالقيمة بدال من المرجع هو أمر معقد أي نسخ الكائنات من

متحول إلى آخر بحيث يشير كال المتحولين إلى كائنين متساويين ولكن منفصلين وليس اإلشارة إلى كائن

واحد. وذلك ألن الكائن الواحد يمكن أن يتضمن على مراجع لكائنات عديدة أخرى موجودة ضمن حقول

هذا الكائن مثال.

التابع ()MemberwiseCloneعلى كل حال هناك طريقة مبسطة لنسخ الكائنات وذلك من خالل المنهج

ا تعريف منهج عام على كائن إن هذا المنهج هو منهج محمي إال انه ال يمكنن System.Objectللصنف

Shallowيستدعي هذا المنهج بسهولة تسمى عملية النسخ المستخدمة في هذا المنهج بالنسخ السطحي

حيث ال يتم هنا أخذ األعضاء ذات األنواع المرجعية بعين االعتبار هذا يعني أن األعضاء المرجعية في

الموجودة ضمن الكائن األصلي وهو غير أمر مجدالكائن الجديد ستشير إلى نفس الكائنات في األعضاء

في كثير من الحاالت وإذا أردنا أن ننسخ الكائن إلى كائن جديد تماما وأعضاء جديدة أيضا فإن علينا

.Deepاستخدام النسخ العميق

فإذا IClonadbleيمكننا التزود بواجهة تسمح لنا بالقيام بذلك بطريقة قياسية وهي تسمى بالواجهة

يعيد هذا المنهج ()Cloneتخدمنا هذه الواجهة فإن علينا أن نزود صنفنا بمنهج واحد فقط يأخذ االسم اس

ويمكننا ان نستخدم أية عمليات نود القيام بها للحصول على هذا الكائن System.Objectقيمة من نوع

نستخدم أنه يمكننا أنوذلك بوضع الشيفرة التي ستقوم بذلك ضمن جسم هذا المنهج في صنفنا هذا يعني

النسخ العميق إذا أردنا ذلك لكن عملية كهذه ستتطلب إلى مجهود قليل منا.

سوف نتحدث عن ذلك بتفصيل أكبر في الفصول القادمة.

Page 75: مستويات مبتدئ و متوسط -ج2

- 74 - إعداد: المهندس حسام الدين الرز

الخالصة:

Summary:

والذي يمثل حجر األساس لما تحدثنا #C والواجهات فيفي هذا الفصل كيفية تعريف األصناف رأينالقد

في التصريح عن اإلضافات والواجهات #Cل نظري في الفصل السابق كما تعرفنا على صيغة عنه بشك

صناف واألباإلضافة إلى مقيدات الوصول التي يمكننا استخدامها والطريقة التي يمكننا وراثة الواجهات

لتحكم بعملية ل والمنغلقة وذلكفي أصنافنا ولقد تعرفنا أيضا على كيفية تعريف األصناف المجردة األخرى

.والتدميرالبناء مناهج إلى تعريف باإلضافةالوراثة من تلك األصناف هذا

ورأينا أنه يمثل الصنف الجذر األساس بجميع System.Objectبعد ذلك ألقينا نظرة خاطفة على الصنف

ري ا ظاهاألصناف التي تعرفنا عليها يقدم هذا الصنف عددا من المناهج التي يمكننا استخدامها بعضه

يمكننا تجاوزه في أصنافنا لقد رأينا أيضا أن الصنف يمكننا من معاملة أي كائن على أنه حالة منه مما

NET.يسمح باستخدام تقنية تعددية األشكال مع أي كائن في

بهدف تسهيل التطوير Visual Studio.NETلقد تناولنا بعد ذلك بعضا من األدوات التي يقدمها

ونافذة مستعرض الكائنات Class Viewت البرمجة كائنية التوجه وقد تضمن ذلك إطار باستخدام تقنيا

Object Browser ولقد طرحنا أيضا طريقة سريعة إلضافة أصناف جديدة إلى المشروع ولقد تعرفنا

يأيضا على كيفية إنشاء المجمعات التي ال يمكننا تنفيذها بصورة مباشرة إال انه يمكننا أن نستخدمها ف

شيفرة مشاريع أخرى.

وما هي األوضاع واالختالف بينهماتحدثنا أيضا عن األصناف المجردة والواجهات ورأينا أوجه التشابه

التي يجب أن نستخدم فيها األصناف المجردة واألوضاع التي يجب ان نستخدم فيها الواجهات.

دا وتحدثنا بخصوص هذا الموضوع حول تناولنا موضوع األنواع المرجعية وأنواع القيمة مجد فقدوأخيرا

واختالفها عن األصناف وقد قادنا موضوع كهذا للحديث عن النسخ السطحي والنسخ Structأنواع البنى

تاب.كالعميق للكائنات وهو موضوع سنتحدث عنه في موضع آخر من هذا ال

ينقل مناهج وهو ما سسوف نتناول في الفصل القادم كيفية تعريف أعضاء األصناف مثل الخصائص وال

إلى المستوى المطلوب في محاولة للبدء بإنشاء تطبيقات حقيقية. #Cالبرمجة كائنية التوجه في

Page 76: مستويات مبتدئ و متوسط -ج2

- 75 - إعداد: المهندس حسام الدين الرز

الفصل الثالث

تعريف أعضاء األصنافوذلك بتعلم كيفية تعريف أعضاء #Cسوف نكمل في هذا الفصل حديثنا عن تعريف األصناف في

األصناف من حقول وخصائص ومناهج.

سوف نبدأ أوال بصيغ تعريف هذه األعضاء وسوف نرى كيفية توليد هذه الصيغ باستخدام معالجات

Visual Studio .NET 2013 المساعدة وسوف نتعلم أيضا كيفية تعديل األعضاء بسرعة وذلك بتحرير

خصائصها.

مثل كيفية عضاءوبعد تغطية أساسيات تعريف األعضاء سوف نتحدث عن تقنيات أكثر تقدما متعلقة باأل

إخفاء أعضاء الصنف األساس واستدعاء أعضاء الصنف األساس التي تم تجاوزها باإلضافة إلى

التعريفات المعششة.

وأخيرا فإننا سنضع جميع ما تعلمناه في هذا الفصل والفصل السابق موضع االستخدام العملي الحقيقي

قادمة من هذا الكتاب إن شاء هللا.وذلك بإنشاء مكتبة أصناف سنعتمد عليها في الفصول ال

تعريف األعضاء:

Member Definitions:

يمكننا تعريف أعضاء الصنف بعد تعريفه مباشرة ويتضمن ذلك تعريف الحقول والمناهج والخصائص

التي تنتمي لهذا الصنف ولكل عضو من أعضاء الصنف مدى الوصولية الخاص به ويمكن تحديد مدى

عضاء الصنف باستخدام أربعة مقيدات وصول فقط:الوصولية في تعريف أ

الوصف مقيد الوصول

public يمكن الوصول إلى العضو من ضمن اية شيفرة

Private ال يمكن الوصول إلى العضو إال من ضمن شيفرة الصنف نفسه

Internal يمكن الوصول إلى العضو من ضمن المشروع )المجمعة( الذي يحوي على تعريفه

فقط.

Protected .ال يمكن الوصول إلى العضو إال من ضمن شيفرة الصنف نفسه أو من صنف مشتق

Page 77: مستويات مبتدئ و متوسط -ج2

- 76 - إعداد: المهندس حسام الدين الرز

وفي تلك الحالة فإن protected internalويمكن لمقيدي الوصول األخيرين أن يستخدما معا مثل

ة داألعضاء المعرفة وفق مقيد الوصول هذا ال يمكن الوصول إليها إال من خالل األصناف المشتقة الموجو

ضمن المشروع نفسه أو ضمن المجمعة نفسها.

وعندئذ فإن األعضاء المعرفة Staticيمكننا التصريح عن الخصائص والمناهج والحقول باستخدام الكلمة

وفقا لهذه الكلمة هي أعضاء ستاتيكية متعلقة بالصنف نفسه وليس بالكائنات المعرفة من هذه األصناف.

تعريف الحقول:

Defining Fields:

تعرف الحقول كما تعرف المتحوالت العادية تماما باإلضافة إلى استخدام أحد مقيدات الوصول التي

شرحناها على سبيل المثال:

class MyClass { public int MyInt;

}

ا الحقل إال وعندئذ فإنه ال يمكن إسناد قيمة إلى هذ readonlyويمكن أن نعرف الحقول باستخدام الكلمة

عند اإلسناد األولي له ويتضمن منهج بناء الصنف على سبيل المثال:

class MyClass { public readonly int MyInt=45;

}

على سبيل المثال: staticوكما قلنا في بداية هذا الفصل فإنه يمكننا تعريف الحقول باستخدام الكلمة

class MyClass { public static int MyInt;

}

حيث يمكن الوصول إلى الحقول الستاتيكية عبر الصنف الذي تنتمي إليه فقط أي باستخدام

MyClass.MyInt ل الكائنات المعرفة من هذا الصنف والمثال بالنسبة للمثال السابق وليس من خال

التالي سيوضح الفرق:

الذي يحوي على مجموعة من الحقول المعرفة كما يلي: Personلنفرض أن لدينا الصنف

class Person { public string FirstName; public string LastName; public int Age; static public int counter;

}

Page 78: مستويات مبتدئ و متوسط -ج2

- 77 - إعداد: المهندس حسام الدين الرز

ومن ثم قمنا باستدعاء الحقول كما يلي: MyEmploedباسم Personفإذا قمنا بإنشاء حالة من الصنف

class Program { static void Main(string[] args) { Person MyEmpolyed = new Person(); MyEmpolyed .FirstName ="Hussam"; MyEmpolyed.LastName = "AL deen AL roz"; MyEmpolyed.Age = 13; MyEmpolyed.counter = 3; Console .WriteLine ("{0}) Person:{1} {2} \nAge:{3}", Person .counter ,MyEmpolyed .FirstName ,MyEmpolyed .LastName ,MyEmpolyed .Age ); Console.ReadLine(); }

}

تم بهذه الطريقة ألنه ال ي counterسوف يظهر لنا خطأ يشير إلى أن الوصول إلى الحقل #Cفإن مترجم

حقل من نوع ستاتيكي والطريقة الصحيحة هي:

class Program { static void Main(string[] args) { Person MyEmpolyee = new Person(); MyEmpolyed .FirstName ="Hussam"; MyEmpolyed.LastName = "AL deen AL roz"; MyEmpolyed.Age = 13; Person.counter = 3; Console .WriteLine ("{0}) Person:{1} {2} \nAge:{3}", Person .counter ,MyEmpolyed .FirstName ,MyEmpolyed .LastName ,MyEmpolyed .Age ); Console.ReadLine(); }

}

مالحظة:

فأيهما يسبق االخر staticو publicفي أسبقية التعريف بين #Cالحظ أنه ال يوجد فرق لدى مترجم

ال يهم.

وكما تالحظ فإن constوباإلضافة إلى ذلك يمكننا إنشاء حقول ذات قيم ثابتة وذلك باستخدام الكلمة

)إن staticاك داع الستخدام المقيد حقول ستاتيكية حسب التعريف وبالتالي ليس هن الحقول الثابتة هي

أي ان الشيفرة التالية صحيحة: استخدامه سيؤدي إلى حدوث خطأ(

namespace ConsoleApplication2 { class Person { public string FirstName;

Page 79: مستويات مبتدئ و متوسط -ج2

- 78 - إعداد: المهندس حسام الدين الرز

public string LastName; public int Age; public const int counter = 3; } class Program { static void Main(string[] args) { Person MyEmpolyee = new Person(); MyEmpolyee .FirstName ="Hussam"; MyEmpolyee.LastName = "AL deen AL roz"; MyEmpolyee.Age = 15; Console .WriteLine ("{0}) Person:{1} {2} \nAge:{3}", Person .counter ,MyEmpolyee .FirstName ,MyEmpolyee .LastName ,MyEmpolyee .Age ); Console.ReadLine(); } } }

تعريف المناهج:

Defining Methods:

تستخدم المناهج نفس شكل التابع باإلضافة إلى إمكانية استخدام مقيدات الوصول وإمكانية استخدام المقيد

static :على سبيل المثال

class MyClass { public string GetString() { return "Here is a String"; }

}

في تعريف المنهج فإنه ال يمكننا الوصول إلى هذا المنهج إال من staticالحظ أننا إذا استخدمنا الكلمة

)كائن( من هذا الصنف. خالل الصنف وليس من خالل حالة

ويمكننا أن نستخدم الكلمات التالية مع تعريف المناهج:

الوصف الكلمة

Virtual ف مشتق يتجاوز شيفرة هذا المنهج(نصالمنهج )أي إنشاء منهج في ز هذايمكن تجاو

Abstract )يجب تجاوز هذا المنهج )ال يمكننا استخدام هذه الكلمة إال في األصناف المجردة فقط

Override يمثل هذا المنهج تجاوزا لمنهج في الصنف األساس

Extern أي أن تعريف هذا المنهج موجود في مكان آخر

Page 80: مستويات مبتدئ و متوسط -ج2

- 79 - إعداد: المهندس حسام الدين الرز

بين الشيفرة التالية كيفية تجاوز منهج في صنف أساس من قبل منهج في صنف آخر:ت

public class MyBaseClass { public virtual void DoSomething() { // Base implementation }

}

public class MyDerivedClass:MyBaseClass { public override void DoSomething() { base.DoSomething(); }

}

يمكن تجاوزه ( public )الكلمة يحتوي على منهج عام MyBaseClassهنا بإنشاء صنف باسم القد قمن

ثم قمنا بإنشاء صنف ومن ()DoSomethingباسم ( virtual )الكلمةمن قبل منهج آخر لصنف مشتق

باسم منهجاويعرف MyBaseClassيرث الصنف MyDerivedClassآخر باسم

DoSomething() الحظ انه يمكننا استخدام المنهجDoSomething() في الصنف المشتق دون

تعريفه ألن هذا المنهج موجود ضمن الصنف المورث )صنف االساس( إال اننا نود لهذا الصنف القيام

.overrideاء أخرى لذا فإننا سنقوم بتجاوز هذا المنهج في الصنف المشتق باسم الكلمة بأشي

مالحظة:

Visual Studio.NETاظهر االتمام التلقائي في overrideبعد كتابتنا لكلمة #Cالحظ ان مترجم

لمة سوف يكمل العبارة واضعا الك #Cوبمجرد اختيارها فإن مترجم ()DoSomethingالكلمة 2013

void وما يليها من عبارات حيث تشير العبارةbase.DoSomething() إلى أن الصنف المشتق

MyDerivedClass قد تجاوز منهج الصنف األساسMyBaseClass وكل ذلك حدث بسبب الكلمة

override زة جديدة موجودة ضمن اإلكمال التلقائي بالباقي وهي مي حيث توقع متنبئVisual

2013Studio.NET ..إال أن اإلبقاء على تلك العبارة سوف يعطل التجاوز كما سنرى الحقا

أيضا وذلك لتحديد أنه ال يمكن sealedيمكنك عندئذ استخدام الكلمة overrideوإذا استخدمت الكلمة

أي أنه MyDerivedClassوضع أية تعديالت إضافية لهذا المنهج في األصناف المشتقة من الصنف

جاوز هذا المنهج من قبل األصناف المشتقة على سبيل المثال:ال يمكن ت

public class MyDerivedClass : MyBaseClass { public sealed override void DoSomething() { base.DoSomething(); }

}

Page 81: مستويات مبتدئ و متوسط -ج2

- 80 - إعداد: المهندس حسام الدين الرز

ال يمكنه تجاوز المنهج MyDerivedClassصنف يرث الصنف أي ن هذا يشير إلى أنإ

DoSomething() وتستخدم الكلمةextern لتوفير تزويد للمنهج خارج المشروع وذلك موضوع متقدم

ولن نتحدث عنه في هذا الكتاب.

تعريف الخصائص:

Defining Properties:

ئص اتعرف الخصائص بنفس األسلوب الذي تعرف وفقه الحقول إال أن هناك أشياء أخرى ضمن الخص

فالخصائص يمكنها القيام بأمور أكبر من مجرد وسيلة لحفظ حالة أو قيمة معينة كما تقوم الحقول بذلك

حيث تتضمن الخصائص على كتلتين برمجيتين يتم تنفيذهما عند قراءة قيمة الخاصية أو عند إسناد قيمة

للخاصية.

ن تعريف تجاهل إحدى هاتين الكتلتين ضمالممكن ومن setو getتعرف هاتين الكتلتان باستخدام الكلمتين

او للكتابة فقط setإهمال الكتلة أن ننشئ خاصية للقراءة فقط عند نه يمكنناالخاصية وفي حالة كهذه فإ

لكن يجب أن نضع واحدا من هاتين الكتلتين على األقل في تعريف الخاصية لكن getعند إهمال الكتلة

خاصية كهذه لذا فإن إنشاء خاصية للكتابة فقط أمر غير مفيد عمليا كما نرى فإنه من غير المنطقي وجود

إال انه ممكن.

أو غيرها متبوعة بنوع الخاصية privateأو publicتتألف البنية األساسية للخاصية من مقيد وصول

:setو getواسمها ومن ثم إحدى أو كال الكتلتين

<accessModifier> <propertyType> <propertyName>

{

Get

{

// code run in read

}

Set

{

// code run in write

}

Page 82: مستويات مبتدئ و متوسط -ج2

- 81 - إعداد: المهندس حسام الدين الرز

}

على سبيل المثال:

class Person { public int MyAge { get { // property get code } set { // property set code } } } الحظ ان السطر األول من تعريف الخاصية مشابه جدا لتعريف الحقول والفرق الواضح هنا هو عدم

ينا كتلة برمجية تتضمن على كتلتين " عند نهاية السطر وبدال من ذلك فإن لد;وجود الفاصلة المنقوطة "

.setو getبرمجيتين معشعشتين ضمنها وهما

إذن كيف سنستخدم هاتين الكتلتين؟

على قيمة معادة من نفس نوع الخاصية أي أنه يجب أن تتضمن getحسنا أوال يجب أن تحتوي الكتلة

.returnعلى تعليمة getالكتلة

معرف ضمن الصنف وعندئذ privateبط الخاصية بحقل خاص وكقاعدة عامة فإنه يجب دائما ان ترت

تصبح مهمة الخاصية التحكم في الوصول إلى هذا الحقل على سبيل المثال:

class Person { private int Age; public int MyAge { get { return Age; } set { // property set code } }

}

ابدا وذلك ألنه معرف وفق مقيد Ageالحظ أن الشيفرة خارج الصنف ال يمكنها الوصول إلى الحقل

MyAgeأن نستخدم الخاصية فإن علينا ولكي نتمكن من قراءة قيمة الحقل privateالوصول الخاص

والتي تمثل مغلفا لهذا الحقل.

Page 83: مستويات مبتدئ و متوسط -ج2

- 82 - إعداد: المهندس حسام الدين الرز

بإسناد قيمة لحقل الخاصية لكن كيف يمكننا الوصول إلى القيمة المسندة من خارج setثانيا تقوم الكتلة

set؟ في الحقيقة إن أية قيمة يتم إسنادها إلى الخاصية التي تحتوي على كتلة setالصنف من خالل الكتلة

وهذا يعني أنه يمكننا الوصول إلى القيمة التي تم إسنادها إلى valueستخزن ضمن بارامتر وهمي باسم

.valueالخاصية من خالل الكلمة

حسنا لقد تعلمنا كيفية قراءة قيمة الخاصية من خالل قراءة الحقل واآلن إليك كيفية تعديل الشيفرة السابقة

من خالل خاصية ذلك الحقل: لكي نتمكن من إسناد قيمة إلى الحقل

class Person { private int Age; public int MyAge { get { return Age; } set { Age = value; } }

}

الخاص من نفس نوع الخاصية والذي يجب أن يكون من نفس نوع الحقل valueيجب ان تكون قيمة

لحفظ القيمة ضمن castالمرتبط بهذه الخاصية أيضا وبالتالي ليس هناك داع للقلق حول استخدام التشكيل

الحقل.

لقوة الحقيقية للخصائص تأتي عندما نبذلإن الخاصية تقوم بأكثر من مجرد غالف للوصول إلى الحقل وا

بالصورة التالية: setتحكما أكبر على األحداث على سبيل المثال يمكننا أن نكتب كتلة

set { if (!(value > 100 || value < 1)) Age = value;

}

وفي حاالت كهذه 100و 0هنا فقط إذا كانت القيمة المسندة إلى الخاصية بين Ageلحقل ستعدل قيمة ا

من المهم جدا أن نحدد السلوك الذي يجب إتباعه عند إدخال قيم خارج هذا المجال أي قيمة خاطئة ويكون

لدينا أربعة خيارات عادة:

)كما في الشيفرة السابقة( شيءعدم القيام بأي

الفتراضية للحقل )إذا كانت هناك قيمة احترافية(إسناد القيمة ا

االستمرار كما لو أنه لم يحدث شيء خاطئ ولكن تسجيل ما حدث للتحليل المستقبلي.

رمي اعتراض.

Page 84: مستويات مبتدئ و متوسط -ج2

- 83 - إعداد: المهندس حسام الدين الرز

وبصورة عامة فإن الخيارين األخيرين هما الخياران المفضالن واختيار أحدهما يعتمد على الكيفية التي

لتحكم الذي يمكننا أن نقدمه لمستخدمي الصنف فرمي اعتراض يقدم سيستخدم وفقها الصنف وما مدى ا

لمستخدمي الصنف تحكما أكبر ويسمح بمعرفة ما يحدث واالستجابة لذلك بصورة مناسبة ويمكننا أن

عادي لذلك على سبيل المثال: Systemنستخدم اعتراض

set { if (!(value > 100 || value < 1)) Age = value; else throw (new ArgumentOutOfRangeException("MyAge", value, "MyAge must be assigned a value between 0 and 100"));

}

في الشيفرة التي تستخدم try..catch..finallyراض كهذا من خالل استخدام البنية ويمكننا معالجة اعت

الخاصية.

أما تسجيل البيانات في الغالب ضمن ملف نصي فيمكن أن يكون مفيدا في حاالت الشيفرة اإلنتاجية والتي

الشيفرة حال يجب أن تحدث المشاكل فيها فهي تسمح للمطورين بتفحص أداء التطبيق وإمكانية تصحي

الحالية عند الضرورة.

ويمكن ان تكون overrideويمكن أن تكون قابلة للتجاوز virtualيمكن أن تكون الخصائص ظاهرية

وهو أمر غير ممكن مع الحقول. abstractمجردة أيضا

تطبيق حول استخدام الحقول والمناهج والخصائص:

.Console Properties Methods Fieldsباسم جديد Consoleقم بإنشاء تطبيق -1

باستخدام أحد الطرق التي شرحناها في الفصل السابق. MyClassأضف صنفا جديدا باسم -2

كما يلي: MyClass.csعدل شيفرة الصنف -3

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Console_Properties_Methods_Fields { public class MyClass { public readonly string Name; private int intVal; public int val { get { return intVal;

Page 85: مستويات مبتدئ و متوسط -ج2

- 84 - إعداد: المهندس حسام الدين الرز

} set { if (value >= 0 && value <= 10) intVal = value; else throw (new ArgumentOutOfRangeException("val", value, "val must be assigned a value between 0 and 10.")); } } public override string ToString() { // return base.ToString(); return "Name:" + Name + "\nval:" + val; } private MyClass ():this("Default Name") { } public MyClass (string newName) { Name = newName; intVal = 0; } } }

كما يلي: csProgram.عدل شيفرة الملف -4

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Console_Properties_Methods_Fields { class Program { static void Main(string[] args) { Console.WriteLine("Creating object myObj..."); MyClass myObj = new MyClass("My Object"); Console.WriteLine("myObj created."); for (int i=-1;i<=0;i++) { try { Console.WriteLine("\nAttempting to assign {0} to myObj.val...", i); myObj.val = i; Console.WriteLine("Value {0} assigned to myObj.val.", myObj.val); } catch (Exception e)

Page 86: مستويات مبتدئ و متوسط -ج2

- 85 - إعداد: المهندس حسام الدين الرز

{ Console.WriteLine("Exception {0} thrown.", e.GetType().FullName); Console.WriteLine("Message:\n\"{0}\"", e.Message); } } Console.WriteLine("\nOutPutting myObj.ToString()..."); Console.WriteLine(myObj); Console.WriteLine("myObj.ToString() OutPut."); Console.ReadLine(); } } }

.F5نفذ الشيفرة بالضغط على زر -5

(3-1الشكل )

كيفية العمل:

How to Work:

MyClass.csالمعرف ضمن الملف MyClassبإنشاء كائن من الصنف ()Mainتقوم شيفرة المنهج

ويجب أن يتم إنشاء حالة من هذا الصنف باستخدام منهج بناء غير افتراضي وذلك ألن منهج البناء

:privateفق المقيد االفتراضي معرف و

private MyClass ():this("Default Name") { }

توضيح:

سيأخذ Nameوذلك للتأكد من أن الحقل this ("Default Name")الحظ أنني استخدمت العبارة

هذا الصنف قيمة إذا تم استدعاء منهج البناء هذا بطريقة أو بأخرى وهو ما يمكن حدوثه عند توريث

Page 87: مستويات مبتدئ و متوسط -ج2

- 86 - إعداد: المهندس حسام الدين الرز

على قيمة دوما ألن عدم القيام بذلك يمكن أن يمثل مصدرا Nameإلى صنف آخر يجب أن يحتوي الحقل

لحدوث األخطاء الحقا.

بما أن هذا Nameلذا فقد استخدمت منهج بناء غير افتراضي يقوم بإسناد قيمة إلى حقل القراءة فقط

سناد قيمة إليه إال من خالل اإلسناد األولي في سطر فإنه ال يمكننا إ readonlyمعرف بالمقيد لالحق

التصريح عنه أو من خالل مناهج بناء الصنف الذي يتبع له فقط ولقد تم أيضا تعريف حقل خاص باسم

intVal.

وهو كائن من myObjللكائن valبمحاولتين إلسناد قيمة إلى الخاصية ()Mainبعد ذلك سيقوم المنهج

-1إلى الصفر أي القيمتان -1تبدأ من القيمة forاستخدمنا لهذا الغرض حلقة ولقد MyClassالصنف

ات الناتجة من عملية اإلسناد تلك ومعالجة االعتراضالكتشاف try..catchاستخدمنا بنية فقط! ولقد 0و

.System سيتم رمي اعتراض من النوع لخاصيةل -1فعند إسناد القيمة

ArgumentOutOfRangeException وسيتم اصطياد هذا االعتراض من خالل كتلةcatch حيث

تم إسناد ي( فسi=0أما الدورة الثانية للحلقة )عندما Consoleتراض على نافذة عستطبع معلومات هذا اال

من خالل تلك الخاصية. 0ة القيم intValبنجاح أي سيأخذ الحقل valإلى الخاصية 0القيمة

إلخراج نص يمثل محتويات الكائن الحظ myObjللكائن ()ToStringننا سنستخدم المنهج فإوأخيرا

في الصنف System.Objectاننا قمنا بتجاوز هذا المنهج وهو منهج موروث من الصنف الجذر

MyClass وذلك كي يقوم بإعطائنا تمثيال نصيا محددا بحسب ما نريد وليس مجرد إعادة سلسلة نصية

ن لم نقم بتجاوزه واستخدمناه كما هو في الصنف إالفتراضي لهذا المنهج تمثل اسم الكائن وهو السلوك ا

System.Object.

public override string ToString() { // return base.ToString(); return "Name:" + Name + "\nval:" + val;

}

()ToStringوذلك ألنه سيمثل تجاوزا للمنهج overrideنهج بالكلمة يجب ان نصرح عن هذا الم

بصورة valتستخدم الشيفرة هنا الخاصية System.Objectالظاهري الموجود في الصنف األساس

إذا أردنا ذلك intValإال أنه يمكننا أن نستخدم الحقل intValمباشرة بدال من استخدام الحقل الخاص

وجيه يجعلنا نفضل استخدام حقل الخاصية او الخاصية نفسها طالما ان ذلك يتم وليس هناك أي سبب

ضمن الصنف نفسه.

مالحظة:

overrideباستخدام الكلمة ()ToStringأثناء التصريح عن المنهج Visual Studio 2013إن

System.Objectسوف يقوم بوضع شيفرة تشير إلى أنك قد تجاوزت هذا المنهج في الصنف األساس

كي يتأكد حقيقة من أن المبرمج يريد تجاوز المنهج Visual Studio 2013وهذا األسلوب الجديد في

Page 88: مستويات مبتدئ و متوسط -ج2

- 87 - إعداد: المهندس حسام الدين الرز

فإذا أبقيت على تلك الشيفرة فلن يحدث التجاوز و إن محوتها أو جعلتها كتعليق في الصنف األساس

فسيتم التجاوز كما تالحظ في الشيفرة السابقة جرب ذلك.

توضيح:

في الصنف بدال من الحقول ك تأثير ضئيل جدا على األداء عند استخدام الخصائص في الحقيقة هنا

ان استخدام الخصائص له بع الفوائد وذلك في الحالة التي سيتم تنفيذ شيفرة ما موجودة مباشرة إال

ضمن كتلتي الخاصية عند اسناد أو قراءة قيمها.

المساعدة في إنشاء أعضاء األصناف: VS 2013معالجات

Visual Studio 2013 Member Wizards:

Visual Studio 2013لقد رأينا في الفصل السابق كيفية استخدام أسلوب مختصر إلنشاء األصناف في

والتي يمكننا استخدامها من اجل Wizardsوهناك عدد من هذه األدوات تسمى بالمعالجات المساعدة

لمعالجات المساعدة التي تمكننا من إضافة الخصائص محتوى األصناف أيضا سنتحدث في هذا القسم عن ا

والمناهج والحقول بسهولة إلى اصنافنا.

والتي نستطيع الحصول عليها بالضغط بزر الفأرة األيمن فوق Class Diagramكما أشرنا إلى نافذة

ربما View Class Diagramنختار الخيار Viewمر ومن ثم من األ Solution Explorerنافذة

عن سبب ذكرنا لهذه النافذة في الحقيقة إن استخدام المعالجات المساعدة للحقول والمناهج تساءلت

كما هو الحال مع المعالج المساعد لألصناف Solution Explorerوالخصائص ال يتم من خالل نافذة

.Class Diagramإنما يتم من خالل نافذة

معالج إضافة منهج:

The Add Method Wizard:

يمكننا اختيار أحد األصناف الموجودة في مشروعنا ثم الضغط عليها Class Diagramبعد فتح نافذة

تظهر قائمة فرعية تحوي على العديد من Addبزر الفأرة األيمن حيث تظهر قائمة منسدلة من األمر

.Addر ( يظهر قائمة الخيارات الفرعية لألم3-2الشكل ) Methodالخيارات نختار منها الخيار

Page 89: مستويات مبتدئ و متوسط -ج2

- 88 - إعداد: المهندس حسام الدين الرز

(3-2الشكل )

يمكننا المعالج المساعد للمناهج من إضافة منهج إلى الصنف بسهولة ويسر ويمكننا تحقيق ذلك من خالل

من Methodعند اختيار األمر الذي يتغير Class Diagramتغير شكل صندوق الصنف في نافذة

(.3-3القائمة الفرعية حيث يظهر الشكل )

(3-3الشكل )

لكن MyClassنكون بذلك قد أضفنا منهجا جديدا لصنفنا Enterاسم للمنهج وضغط الزر وباختيار

السؤال هنا ما هي صفات هذا المنهج هل هو عام أم خاص أم ..

سنجد ضمن شيفرة هذا الصنف قد أضيفت شيفرة منهج جديد باسم MyClass.csلنذهب إلى الملف

Method :كما يلي

public void Method() { throw new System.NotImplementedException();

}

Page 90: مستويات مبتدئ و متوسط -ج2

- 89 - إعداد: المهندس حسام الدين الرز

وصولية هذا المنهج. بالتأكيد مدى لكن هل يمكننا تغيير Publicكما ترى من الشيفرة فإن هذا المنهج عام

لشجرة من ا Methodثم قم باختيار الكلمة Class Diagramلكن كيف. ال تقلق عد إلى النافذة

Methods وانظر للزاوية اليمنى السفلى نعم انها نافذة الخصائصProperties حيث ستظهر كما في

(.3-4الشكل )

توفر نافذة الخصائص إمكانية الوصول إلى جميع المزايا التي يمكننا استخدامها إلنشاء المناهج فيمكننا

خالل القيم المعادة من نوع وتحديد Access القائمة المنبثقةهنا تحديد مستوى الوصولية من خالل مربع

كما يمكننا إضافة مقيدات وصول Nameوكذلك اسم المنهج من خالل مربع النص type القائمة المنبثقة

يمكننا تحديد وباإلضافة لذلك Modifiersأخرى من خالل مربعات االختيارات أسفل التبويب

نالحظ بعد Method( وذلك باختيار المنهج 3-5الشكل ) Class Detailsالبارامترات باستخدام النافذة

ظهور لألسفل منه قوسين وإلى جانبه بخط رمادي كلمة Methodالضغط على السهم على يسار كلمة

<add parameter> .

(3-4الشكل )

حديد وبت البارامترمادي يظهر محرر نصوص من خالله نستطيع تحديد اسم عند الضغط مكان النص الر

نستطيع تحديد فيما إذا كان Modifierنستطيع تحديد نوع البارامتر وأسفل الكلمة Typeفل الكلمة أس

نستطيع Summaryكما أنه أسفل الكلمة paramsأو مصفوفة بارامترات outأو refالبارامتر من نوع

ع تعليقات سنتحدث عن هذا الحقا الحظ أيضا أنه يمكننا أن نض XMLكتابة تعليقات مصدرية بلغة

XML Documentationللمنهج الذي أضفناه من نافذة الخصائص تحت التبويب XMLمصدرية بلغة

إن تفعيل مربع االختيار سيؤدي Hideوجود مربع اختيار أسفل الكلمة Class Detailsالحظ في النافذة

Page 91: مستويات مبتدئ و متوسط -ج2

- 90 - إعداد: المهندس حسام الدين الرز

ندوق ويظهر رمز ضمن ص Class Diagramفي نافذة MyClassإلى إخفاء العضو من صندوق

MyClass .يشير إلى أن هناك أعضاء مخفية جرب ذلك

(3-5الشكل )

وبالطبع فإن معالج إضافة المناهج ال يوفر لنا شيفرة جسم المنهج وإنما يوفر البنية األساسية للمنهج ويقلل

من أخطاء كتابة تعريف المنهج.

معالج إضافة خاصية:

The Add Property Wizard:

يمكننا اختيار أحد األصناف الموجودة في مشروعنا ثم الضغط عليها Class Diagramبعد فتح نافذة

تظهر قائمة فرعية تحوي على العديد من Addبزر الفأرة األيمن حيث تظهر قائمة منسدلة من األمر

.Add( يظهر قائمة الخيارات الفرعية لألمر 3-6الشكل ) Propertyالخيارات نختار منها الخيار

(3-6)الشكل

يمكننا المعالج المساعد للخصائص من إضافة خاصية إلى الصنف بسهولة ويسر ويمكننا تحقيق ذلك من

Propertyالذي يتغير عند اختيار األمر Class Diagramخالل تغير شكل صندوق الصنف في نافذة

(.3-7من القائمة الفرعية حيث يظهر الشكل )

Page 92: مستويات مبتدئ و متوسط -ج2

- 91 - إعداد: المهندس حسام الدين الرز

(3-7الشكل )

لكن MyClassلصنفنا خاصية جديدةنكون بذلك قد أضفنا Enterوضغط الزر للخاصيةوباختيار اسم

هل هو عام أم خاص أم .. خاصيةالسؤال هنا ما هي صفات هذا ال

اسم ب ةجديد خاصيةسنجد ضمن شيفرة هذا الصنف قد أضيفت شيفرة MyClass.csلنذهب إلى الملف

Property :كما يلي

public int Property { get { throw new System.NotImplementedException(); } set { }

}

لكن هل يمكننا تغيير مدى Publicعام ما ترى من الشيفرة فإن هذه الخاصية تحمل نوع وصوليةك

ثم قم باختيار الكلمة Class Diagram. بالتأكيد لكن كيف. ال تقلق عد إلى النافذة ية هذه الخاصيةوصول

Property من الشجرةProperties وانظر للزاوية اليمنى السفلى نعم انها نافذة الخصائص

Properties ( 3-8حيث ستظهر كما في الشكل.)

(3-8الشكل )

Page 93: مستويات مبتدئ و متوسط -ج2

- 92 - إعداد: المهندس حسام الدين الرز

يمكننا ف يمكننا استخدامها إلنشاء الخصائص لوصول إلى جميع المزايا التيتوفر نافذة الخصائص إمكانية ا

وتحديد نوع القيم المعادة من خالل Accessهنا تحديد مستوى الوصولية من خالل مربع القائمة المنبثقة

كما يمكننا إضافة مقيدات Nameمن خالل مربع النص وكذلك اسم الخاصية typeالقائمة المنبثقة

الحظ أيضا أنه يمكننا أن نضع Modifiersخرى من خالل مربعات االختيارات أسفل التبويب وصول أ

XMLالذي أضفناه من نافذة الخصائص تحت التبويب خاصيةلل XMLتعليقات مصدرية بلغة

Documentation.

(.3-9كل )الش Class Detailsوباإلضافة لذلك يمكننا تحديد كل ما تحدثنا عنه اآلن من خالل النافذة

(3-9الشكل )

ية للخاصيةإنما يوفر البنية األساسو لج إضافة الخصائص ال يوفر لنا شيفرة جسم الخاصيةوبالطبع فإن معا

.يقلل من أخطاء كتابة تعريف الخاصيةو

معالج إضافة حقل:

The Add Field Wizard:

دة في مشروعنا ثم الضغط عليها يمكننا اختيار أحد األصناف الموجو Class Diagramبعد فتح نافذة

تظهر قائمة فرعية تحوي على العديد من Addبزر الفأرة األيمن حيث تظهر قائمة منسدلة من األمر

.Add( يظهر قائمة الخيارات الفرعية لألمر 3-10الشكل ) Fieldالخيارات نختار منها الخيار

(3-10الشكل )

Page 94: مستويات مبتدئ و متوسط -ج2

- 93 - إعداد: المهندس حسام الدين الرز

حقل إلى الصنف بسهولة ويسر ويمكننا تحقيق ذلك من خالل يمكننا المعالج المساعد للحقول من إضافة

من القائمة Fieldالذي يتغير عند اختيار األمر Class Diagramتغير شكل صندوق الصنف في نافذة

(.3-11الفرعية حيث يظهر الشكل )

(3-11الشكل )

لكن السؤال MyClassنا نكون بذلك قد أضفنا حقل جديدة لصنف Enterوباختيار اسم للحقل وضغط الزر

هنا ما هي صفات هذا الحقل هل هو عام أم خاص أم ..

سنجد ضمن شيفرة هذا الصنف قد أضيفت شيفرة حقل جديدة باسم MyClass.csلنذهب إلى الملف

Field :كما يلي

field; int private

كن هل يمكننا تغيير مدى ل Privateكما ترى من الشيفرة فإن هذه الحقل يحمل نوع وصولية خاص

ثم قم باختيار الكلمة Class Diagramوصولية هذا الحقل. بالتأكيد لكن كيف. ال تقلق عد إلى النافذة

Field من الشجرةFields وانظر للزاوية اليمنى السفلى نعم انها نافذة الخصائصProperties حيث

(.3-12ستظهر كما في الشكل )

(3-12الشكل )

Page 95: مستويات مبتدئ و متوسط -ج2

- 94 - إعداد: المهندس حسام الدين الرز

ذة الخصائص إمكانية الوصول إلى جميع المزايا التي يمكننا استخدامها إلنشاء الحقول فيمكننا هنا توفر ناف

وتحديد نوع القيم المعادة من خالل Accessتحديد مستوى الوصولية من خالل مربع القائمة المنبثقة

إضافة مقيدات كما يمكننا Nameوكذلك اسم الخاصية من خالل مربع النص typeالقائمة المنبثقة

الحظ أيضا أنه يمكننا أن نضع Modifiersوصول أخرى من خالل مربعات االختيارات أسفل التبويب

XMLللحقل الذي أضفناه من نافذة الخصائص تحت التبويب XMLتعليقات مصدرية بلغة

Documentation.

(. 3-13الشكل ) Class Details وباإلضافة لذلك يمكننا تحديد كل ما تحدثنا عنه اآلن من خالل النافذة

(3-13الشكل )

بناء:الهج امعالج إضافة من

The Add constructor Wizard:

يمكننا اختيار أحد األصناف الموجودة في مشروعنا ثم الضغط عليها Class Diagramبعد فتح نافذة

ة تحوي على العديد من تظهر قائمة فرعي Addبزر الفأرة األيمن حيث تظهر قائمة منسدلة من األمر

.Add( يظهر قائمة الخيارات الفرعية لألمر 3-14الشكل ) Constructorالخيارات نختار منها الخيار

(3-14الشكل )

Page 96: مستويات مبتدئ و متوسط -ج2

- 95 - إعداد: المهندس حسام الدين الرز

يمكننا المعالج المساعد لمناهج البناء من إضافة منهج بناء إلى الصنف بسهولة ويسر ويمكننا تحقيق ذلك

الذي يتغير عند اختيار األمر Class Diagramافذة من خالل تغير شكل صندوق الصنف في ن

Constructor ( 3-15من القائمة الفرعية حيث يظهر الشكل.)

(3-15الشكل )

سنجد ضمن شيفرة هذا الصنف قد أضيفت شيفرة منهج بناء افتراضي MyClass.csلنذهب إلى الملف

كما يلي: MyClassجديدة باسم MyClassللصنف

public MyClass() { throw new System.NotImplementedException();

}

هل يمكننا تغيير مدى لكن publicيحمل نوع وصولية خاص منهج البناء هذاكما ترى من الشيفرة فإن

ثم قم باختيار الكلمة Class Diagram. بالتأكيد لكن كيف. ال تقلق عد إلى النافذة وصولية منهج البناء هذا

MyClass من الشجرةMethods وانظر للزاوية اليمنى السفلى نعم انها نافذة الخصائصProperties

(.3-16حيث ستظهر كما في الشكل )

(3-16الشكل )

Page 97: مستويات مبتدئ و متوسط -ج2

- 96 - إعداد: المهندس حسام الدين الرز

فيمكننا ناءمناهج البتوفر نافذة الخصائص إمكانية الوصول إلى جميع المزايا التي يمكننا استخدامها إلنشاء

الحظ أيضا أنه يمكننا أن نضع Accessهنا تحديد مستوى الوصولية من خالل مربع القائمة المنبثقة

XMLالذي أضفناه من نافذة الخصائص تحت التبويب منهج البناءل XMLتعليقات مصدرية بلغة

Documentation.

(. 3-17الشكل ) Class Detailsوباإلضافة لذلك يمكننا تحديد كل ما تحدثنا عنه اآلن من خالل النافذة

من بناء مناهج بناء غير افتراضية عن طريق إضافة بارامترات كما Class Detailsكما تمكننا النافذة

تعلمنا ذلك سابقا في معالج إنشاء المناهج.

(3-17الشكل )

:التدميرهج امعالج إضافة من

The Add constructor Wizard:

يمكننا اختيار أحد األصناف الموجودة في مشروعنا ثم الضغط عليها Class Diagramبعد فتح نافذة

تظهر قائمة فرعية تحوي على العديد من Addبزر الفأرة األيمن حيث تظهر قائمة منسدلة من األمر

.Add( يظهر قائمة الخيارات الفرعية لألمر 3-18الشكل ) Destructorالخيارات نختار منها الخيار

(3-18الشكل )

Page 98: مستويات مبتدئ و متوسط -ج2

- 97 - إعداد: المهندس حسام الدين الرز

إلى الصنف بسهولة ويسر ويمكننا تحقيق تدميرمن إضافة منهج التدمير يمكننا المعالج المساعد لمناهج

الذي يتغير عند اختيار األمر Class Diagramذلك من خالل تغير شكل صندوق الصنف في نافذة

Destructor ( 3-19من القائمة الفرعية حيث يظهر الشكل.)

(3-19الشكل )

نف للص تدمير سنجد ضمن شيفرة هذا الصنف قد أضيفت شيفرة منهج MyClass.csى الملف لنذهب إل

MyClass جديدة باسم~MyClass :كما يلي

~MyClass() { throw new System.NotImplementedException();

}

:معالج إضافة الثوابت

The Add Constant Wizard:

يمكننا اختيار أحد األصناف الموجودة في مشروعنا ثم الضغط عليها Class Diagramد فتح نافذة بع

تظهر قائمة فرعية تحوي على العديد من Addبزر الفأرة األيمن حيث تظهر قائمة منسدلة من األمر

.Addمر ( يظهر قائمة الخيارات الفرعية لأل3-20الشكل ) Constantالخيارات نختار منها الخيار

(3-20الشكل )

Page 99: مستويات مبتدئ و متوسط -ج2

- 98 - إعداد: المهندس حسام الدين الرز

إلى الصنف بسهولة ويسر ويمكننا تحقيق ذلك من خالل ثوابتمن إضافة للثوابتيمكننا المعالج المساعد

من Constantالذي يتغير عند اختيار األمر Class Diagramتغير شكل صندوق الصنف في نافذة

(.3-21القائمة الفرعية حيث يظهر الشكل )

(3-21الشكل )

نف للص ثابت جديدسنجد ضمن شيفرة هذا الصنف قد أضيفت شيفرة MyClass.csنذهب إلى الملف ل

MyClass باسمConstant :كما يلي

Constant = 1; int const private

ويحمل قيمة intوهو من النوع privateيحمل نوع وصولية خاص الثابت كما ترى من الشيفرة فإن هذا

.ونوع البيانات وقيمته لكن هل يمكننا تغيير مدى وصولية هذا الحقل 1ي افتراضية تساو

من الشجرة Constantثم قم باختيار الكلمة Class Diagramبالتأكيد لكن كيف. ال تقلق عد إلى النافذة

Fields وانظر للزاوية اليمنى السفلى نعم انها نافذة الخصائصProperties حيث ستظهر كما في الشكل

(22-3.)

يمكننا ف تي يمكننا استخدامها إلنشاء الثوابتتوفر نافذة الخصائص إمكانية الوصول إلى جميع المزايا ال

تغيير اسم الحظ أيضا أنه يمكننا Accessهنا تحديد مستوى الوصولية من خالل مربع القائمة المنبثقة

وتغيير initial valeمربع النص كما يمكننا تغيير قيمته من خالل Nameالثابت من خالل مربع النص

من مربع Modifiersكما يمكننا تغيير مدى الوصولية من تبويب Typeنوع البيانات من مربع النص

من نافذة لهذا الثابت XMLأن نضع تعليقات مصدرية بلغة كما يمكننا Constant Kindالقائمة المنبثقة

.XML Documentationالخصائص تحت التبويب

Page 100: مستويات مبتدئ و متوسط -ج2

- 99 - إعداد: المهندس حسام الدين الرز

(3-22شكل )ال

(. 3-23الشكل ) Class Detailsوباإلضافة لذلك يمكننا تحديد كل ما تحدثنا عنه اآلن من خالل النافذة

وقد تدثنا عن هذا الخيار سابقا Hideوجود مربع اختيار أسفل الكلمة Class Detailsالحظ في النافذة

(3-23الشكل )

سوف نتحدث عنه أثناء Eventلقائمة وهو معالج األحداث وبذلك يتبقى لدينا المعالج األخير من هذه ا

.#Cحديثنا عن األحداث في لغة

مالحظة:

في بزر الفأرة األيمن من استخدام معالجات مساعدة أخرى قم بالنقر Class Diagramتمكننا نافذة

ة فرعية فنالحظ ظهور قائم Addاألمر باختيارثم قم Class Diagramالمساحة البيضاء من نافذة

إلنشاء األصناف (3-42الشكل ) على مجموعة من األوامر التي تستدعي المعالجات المساعدةتحوي

Class والتعداداتEnum والواجهاتInterface واألصناف المجردةAbstract Class

ال تقلق سوف نتعرف Commentالتعليقات المصدرية Structالبنى و Delegateوالمفوضات

ها.عليها جميع

Page 101: مستويات مبتدئ و متوسط -ج2

- 100 - إعداد: المهندس حسام الدين الرز

(3-24الشكل )

:معالج إضافة األصناف

The Add Class Wizard:

(.3-25فيظهر صندوق حوار كما في الشكل ) Addمن القائمة الفرعية ألمر Classقم باختيار الخيار

(3-25الشكل )

كما يمكننا من تحديد Class nameيمكننا صندوق الحوار من اختيار اسم الصنف من صندوق النص

الحظ أن صندوق الحوار هذا له خياران إما Accessوصولية الصنف هذا من صندوق االختيار مدى

فإذا اخترنا خيار Add to exiting fileملف موجود لأو إضافة Create new fileإنشاء ملف جديد

(.3-26الشكل ) Solution Explorerإلى نافذة Class1.csإنشاء ملف سوف يضاف ملف جديد باسم

Page 102: مستويات مبتدئ و متوسط -ج2

- 101 - إعداد: المهندس حسام الدين الرز

(3-26الشكل )

يمكننا تحرير خصائص صنفنا الجديد من نافذة Class Diagramمن نافذة Class1صندوق باختيار

properties حيث نتمكن من تبويبModifiers من الوصول إلى مدى الوصوليةInheritance

Modifiers نا سابقا ما تعلمحيث يمكننا تحديد فيما إذا كان الصنف مغلقا أو مجردا أو صنفا ستاتيكيا ك

تمكننا نافذة الخصائص من تحرير العديد من األمور لقد أصبحت قادرا على التعامل معها جميعها.

بالضغط بز الفأرة األيمن يمكننا Class Diagramوكما تعلمنا سابقا يمكننا اختيار الصنف من نافذة

إضافة المزيد من الكائنات لهذا الصنف.

:معالج إضافة التعدادات

The Add Enum Wizard:

(.3-27فيظهر صندوق حوار كما في الشكل ) Addمن القائمة الفرعية ألمر Enumقم باختيار الخيار

(3-27الشكل )

Page 103: مستويات مبتدئ و متوسط -ج2

- 102 - إعداد: المهندس حسام الدين الرز

كما يمكننا من تحديد Enum nameيمكننا صندوق الحوار هذا من اختيار اسم التعداد من صندوق النص

الحظ أن صندوق الحوار هذا له خياران إما Access مدى وصولية التعداد هذا من صندوق االختيار

فإذا اخترنا خيار Add to exiting fileأو إضافة ملف موجود Create new fileإنشاء ملف جديد

(.3-28الشكل ) Solution Explorerإلى نافذة Enum1.csإنشاء ملف سيضاف ملف جديد باسم

(3-28الشكل )

الجديد من نافذة يمكننا تحرير خصائص تعدادنا Class Diagramذة من ناف Enum1باختيار صندوق

properties كما تعلمنا سابقا تمكننا نافذة الخصائص من تحرير العديد من األمور لقد أصبحت قادرا

على التعامل معها جميعها.

يمكننا حيث األيمن بالضغط بز الفأرة Class Diagramمن نافذة تعدادوكما تعلمنا سابقا يمكننا اختيار ال

.(3-29أعضاء هذا العداد الشكل )إضافة

(3-29الشكل )

Enum1يمكننا إضافة عضو جديد للتعداد حيث يتغير شكل صندوق التعداد Memberوباختيار الخيار

حيث يمكننا تحديد اسم العضو الجديد في التعداد وتمكننا نافذة الخصائص (3-30كما في الشكل )

Page 104: مستويات مبتدئ و متوسط -ج2

- 103 - إعداد: المهندس حسام الدين الرز

properties ونافذةClass Details من تحرير اسم العضو أيضا كما تمكننا من تحديد قيمةvalue لهذا

العضو راجع تعريف التعدادات.

(3-30الشكل )

:معالج إضافة الواجهات

The Add Interface Wizard:

لشكل فيظهر صندوق حوار كما في ا Addمن القائمة الفرعية لألمر Interfaceقم باختيار الخيار

(31-3.)

(3-31الشكل )

كما يمكننا من Interface nameيمكننا صندوق الحوار هذا من اختيار اسم الواجهة من صندوق النص

الحظ أن صندوق الحوار هذا له خياران Accessمن صندوق االختيار الواجهة هذهتحديد مدى وصولية

فإذا اخترنا Add to exiting fileجود أو إضافة ملف مو Create new fileإما إنشاء ملف جديد

الشكل Solution Explorerإلى نافذة Interface1.csخيار إنشاء ملف سيضاف ملف جديد باسم

(32-3.)

Page 105: مستويات مبتدئ و متوسط -ج2

- 104 - إعداد: المهندس حسام الدين الرز

(3-32الشكل )

من ةالجديد الواجهةيمكننا تحرير خصائص Class Diagramمن نافذة Interface1باختيار صندوق

سابقا تمكننا نافذة الخصائص من تحرير العديد من األمور لقد أصبحت قادرا كما تعلمنا propertiesنافذة

على التعامل معها جميعها.

بالضغط بز الفأرة األيمن حيث Class Diagramمن نافذة لواجهةوكما تعلمنا سابقا يمكننا اختيار ا

(.3-33الشكل )ه الواجهة يمكننا إضافة أعضاء هذ

(3-33الشكل )

Abstract Classاترك لك التعرف على المعالجات المساعدة إلضافة األصناف المجردة واآلن سوف

Commentأما المعالج المساعد إلضافة التعليقات المصدرية Structوالبنى Delegateوالمفوضات

سوف نتحدث عنه في المستقبل.

مالحظة:

ة ضمن ملفات مفصلة ضمن جسم الحظ ان جميع هذه المعالجات تمككنا من إضافة المكونات الجديد

.Visual Studio 2013المشروع أو الحل وهذا أسلوب جديد في

Page 106: مستويات مبتدئ و متوسط -ج2

- 105 - إعداد: المهندس حسام الدين الرز

لكن السؤال هنا هل هذه الملفات تتبع لمجال أسماء مختلف الجواب سيأتيك إذا ما ضغط على أحد هذه

Struct1.cs وليكن ملفلتظهر نافذة الشيفرة لهذا الملف Solution Explorerالملفات ضمن نافذة

فيظهر الكود التالي:

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace Console_Properties_Methods_Fields { public struct Struct1 { }

}

نا كتبناها أسفل مجال األسماء نالحظ من خالل الشيفرة أنها تتبع لمجال أسماء مشروعنا أي وكن

لمشروعنا.

مواضيع أخرى متعلقة بأعضاء األصناف:

Additional Class Member Topics:

واآلن وبعد أن تناولنا أساسيات تعريف األعضاء فقد حان الوقت لتناول مواضيع أكثر تقدما وسوف نتحدث

في هذا القسم عن:

.إخفاء مناهج الصنف األساس

الصنف األساس المخفية أو التي تم تجاوزها. استدعاء مناهج

.تعريفات األنواع المعششة

إخفاء مناهج الصنف األساس:

Hiding Base Class Methods:

ننا نرث أيضا شيفرة هذا العضو إذا كان منهجا راثة عضو غير مجرد من صنف أساس فإعندما نقوم بو

تجاوز شيفرة هذا العضو بتعريف نفس العضو ولكن أو خاصية طبعا وإذا ورثنا عضوا ظاهريا يمكننا أن ن

وبغض النظر عما إذا كان العضو ظاهريا أم ال فإن بإمكاننا حجب شيفرة overrideباستخدام الكلمة

األعضاء المورثة إن ذلك مفيد أحيانا على سبيل المثال يمكن لهذا العضو أن يحتوي على شيفرة عامة ال

تتوافق مع الصنف المشتق مثال.

ويمكننا القيام بذلك كما في المثال التالي:

Page 107: مستويات مبتدئ و متوسط -ج2

- 106 - إعداد: المهندس حسام الدين الرز

public class MyBaseClass { public void DoSomething() { // Base implementation } } public class MyDerivedClass:MyBaseClass { public void DoSomething() { // derived class implementation, hides base implementation }

}

الحظ أن الصنف األساس MyDerivedClassللصنف MyBaseClassلقد قمنا هنا بتوريث الصنف

ويمكننا حجب شيفرة هذا المنهج في الصنف ()DoSomethingيحتوي على المنهج

MyDerivedClass جرد إنشاء المنهج نفسه ضمن هذا الصنف.مب

على الرغم من أن هذه الشيفرة تعمل بصورة جيدة إال أنها ستولد تحذيرا يبين أننا قمنا بحجب عضو في

الصنف األساس وسيعطينا هذا التحذير فرصة تصحيح األمور إذا كنا قد قمنا بعملية الحجب تلك عن

التحذير. طريق الخطأ وهذا هو مضمون رسالة

Warning 1 'ConsoleApplication3.MyDerivedClass.DoSomething()' hides

inherited member 'ConsoleApplication3.MyBaseClass.DoSomething()'. Use the new

keyword if hiding was intended

ذلك باستخدام على كل حال هناك طريقة صريحة يمكننا استخدامها لحجب أعضاء الصنف األساس و

كما في الشيفرة التالية:كما أشارت إلى ذلك رسالة التحذير newالكلمة

public class MyBaseClass { public void DoSomething() { // Base implementation } } public class MyDerivedClass : MyBaseClass { new public void DoSomething() { // Derived class implementation, hides base implementation }

}

ستعمل هذه الشيفرة تماما كما في الشيفرة السابقة إال أننا هنا سنتجنب رسالة التحذير.

الصنف األساس ولنأخذ وتجاوز أعضاءننوه إلى الفرق بين حجب وعند هذه النقطة من الجدير بنا أن

الشيفرة التالية مثال.

Page 108: مستويات مبتدئ و متوسط -ج2

- 107 - إعداد: المهندس حسام الدين الرز

public class MyBaseClass { public virtual void DoSomething() { Console.WriteLine("Base imp"); } } public class MyDerivedClass : MyBaseClass { public override void DoSomething() { Console.WriteLine("Derived imp"); }

}

في الصنف األساس واستخدمنا تزويدا آخر له ضمن ()DoSomethingلقد قمنا هنا بتجاوز المنهج

MyDerivedClassه الحالة فإن أي كائن من الصنف وفي هذ MyDerivedClassالصنف المشتق

سيستخدم التزويد الجديد للمنهج وحتى إن قمنا باستدعاء هذا المنهج من خالل الصنف األساس لكائن من

الصنف المشتق )أي باستخدام تعددية األشكال(:

public class MyBaseClass { public virtual void DoSomething() { Console.WriteLine("Base imp"); } } public class MyDerivedClass : MyBaseClass { public override void DoSomething() { Console.WriteLine("Derivde imp"); } } class Program { static void Main(string[] args) { MyDerivedClass myObj = new MyDerivedClass(); MyBaseClass myBaseObj; myBaseObj = myObj; myBaseObj.DoSomething();

}

طينا الخرج التالي:إن تنفيذ هذه الشيفرة سوف يع

Page 109: مستويات مبتدئ و متوسط -ج2

- 108 - إعداد: المهندس حسام الدين الرز

(3-34الشكل )

في الصنف األساس: ()DoSomethingأما إذا قمنا بحجب المنهج

public class MyBaseClass { public virtual void DoSomething() { Console.WriteLine("Base imp"); } } public class MyDerivedClass : MyBaseClass { new public void DoSomething() { Console.WriteLine("Derived imp"); } } class Program { static void Main(string[] args) { MyDerivedClass myObj = new MyDerivedClass(); MyBaseClass myBaseObj; myBaseObj = myObj; myBaseObj.DoSomething();

}

(.3-35فإن النتيجة ستكون كما في الشكل )

(3-35الشكل )

مالحظة:

وذلك Virtualالحظ أنه ليس من الضروري أن يكون العضو ظاهريا أي تم تعريفه باستخدام الكلمة

تجاوزه.ك أن تدرك الفرق بين حجب العضو وكي نتمكن من حجبه في الصنف المشتق علي

Page 110: مستويات مبتدئ و متوسط -ج2

- 109 - إعداد: المهندس حسام الدين الرز

لمنهج في الصنف األساس محجوبة إال أنه مازال بإمكاننا الوصول الحظ هنا على الرغم من أن شيفرة ا

إليها وذلك طبعا من خالل الصنف األساس وباستخدام تقنية تعددية األشكال.

استدعاء مناهج الصنف األساس المحجوبة والتي تم تجاوزها:

Calling Overridden or Hidden Base Class Methods:

في الصنف األساس من الصنف المشتق فإن بإمكاننا الوصول إلى ذلك سواء تم تجاوز او حجب عضو

العضو من داخل الصنف المشتق وهناك عدة أوضاع نحتاج فيها إلى ذلك على سبيل المثال:

عندما نود حجب العضو العام الموروث عن مستخدمي صنف مشتق ما مع إمكانية الوصول إلى

وظائف هذا العضو ضمن الصنف.

ضافة تزويد للعضو الظاهري الموروث بدال من مجرد استبداله بتزويد جديد أي عندما نود إ

والتي تشير إلى الصنف األساس دائما على baseتجاوزه ولتحقيق ذلك يمكننا استخدام الكلمة

سبيل المثال:

public class MyBaseClass { public virtual void DoSomething() { // Base implementation } } public class MyDerivedClass : MyBaseClass { public override void DoSomething() { // Derived class implementation extends base class // implementation base.DoSomething(); // More derived class implementatio }

}

والذي يمثل MyBaseClassالموجود ضمن الصنف ()DoSomethingتقوم هذه الشيفرة بتنفيذ منهج

الموجود ()DoSomethingوذلك من ضمن المنهج MyDerivedClassالصنف األساس للصنف

.MyDerviedClassضمن الصنف

مها مع عضو ستاتيكي.تعمل وفقا لحاالت الكائنات فإنه ال يمكننا استخدا baseوباعتبار أن الكلمة

:thisالكلمة

This keyword:

Page 111: مستويات مبتدئ و متوسط -ج2

- 110 - إعداد: المهندس حسام الدين الرز

التي يمكننا استخدامها للوصول إلى أعضاء الصنف األساس فإن هناك الكلمة baseوباإلضافة إلى الكلمة

this والتي يمكننا استخدامها للوصول إلى أعضاء الصنف الحالي وهي كالكلمةbase حيث ال تستخدم

ما نستخدم هذه الكلمة فإننا نشير إلى حالة من الصنف وليس إلى الصنف إال مع كائنات الصنف إي عند

بصورة عامة وبالتالي فإنه ال يمكننا الوصول إلى األعضاء الستاتيكية بواسطتها.

إن أكثر االستخدامات أهمية لهذه الكلمة عندما نمرر مرجعا للكائن الحالي من خاللها كما يلي:

public void DoSomething() { MyTargetClass myObj = new MyTargetClass(); myObj.DoSomethingWith(this);

}

ومن ثم قمنا باستدعاء المنهج MyTargetClassمن النوع myObjلقد قمنا هنا بإنشاء كائن باسم

myObj.DoSomethingWith() ارامتر واحدا حيث يمثل هذا البارامتر كائنا لهذا الكائن بحيث يأخذ ب

وبالتالي يمكن لنوع البارامتر أن يكون من DoSomethingمن نفس النوع الذي يحتوي على المنهج

نفس نوع الصنف الحالي أو يمكن أن يكون من نفس نوع الصنف األساس للصنف الحالي أو من نوع

.System.Objectالنوع واجهة يستخدمها الصنف ويمكن أن يكون طبعا من

تعريفات األنواع المعششة:

Nested Type Definitions:

وباإلضافة إلى تعريف األنواع كاألصناف في فضاءات األسماء فإنه يمكننا أن نعرف األصناف ضمن

أصناف أخرى وإذا أردنا القيام بذلك عندئذ سنتمكن من استخدام مجال تام من مقيدات الوصول لتعريف

ويمكننا ان نستخدم internalوالمقيد publicوأعضائها وذلك بدال من مجرد استخدام المقيد األصناف

أيضا إلخفاء تعريف النوع الموروث من صنف أساس. newالكلمة

وتعريف صنفا معششا ضمنه باسم MyClassعلى سبيل المثال تقوم الشيفرة التالية بتعريف الصنف

MyNestedClass:

public class MyBaseClass { public class MyNestedClass { public int nestedClassField; }

}

فإن علينا MyClassمن خارج الصنف MyNestedClassوبالتالي إذا أردنا أن ننشئ كائنا من النوع

أن نبين اسم النوع التام كما يلي:

();myNestedClass.MyBaseClass newmyObj = myNestedClass.MyBaseClass

Page 112: مستويات مبتدئ و متوسط -ج2

- 111 - إعداد: المهندس حسام الدين الرز

privateالحظ أننا لن نتمكن من إنشاء كائن من الصنف المعشعش إذا كان معرفا على انه صنف خاص

أو استخدام مقيد وصول غير متوافق مع الشيفرة في النقطة التي تم فيها إنشاء حالة من الكائن.

بالنسبة للصنف privateيسي لوجود ميزة كهذه يعود إلى تعريف أصناف خاصة أي أنها إن السبب الرئ

الذي يحتويها وبالتالي ال يمكن ألي شيفرة في فضاء األسماء الوصول إلى تلك األصناف عدا الصنف

الذي يحتويها فقط.

التزود بالواجهات:

Interface Implementation:

تعريف الواجهات وتزويد األصناف بها لقد رأينا في الفصل السابق أن سوف نتناول في هذا القسم كيفية

الواجهات تعرف بصورة مشابهة لتعريف األصناف وذلك باستخدام شيفرة كما يلي مثال:

interface IMyInterface { // interface members

}

هناك عددا من االختالفات المهمة بين أعضاء وتعرف أعضاء الواجهة تماما كأعضاء األصناف إال أن

األصناف وأعضاء الواجهة:

ال يمكن استخدام مقيدات الوصول أي الكلمات(puplic,private,protected,internal)

فجميع أعضاء الواجهة هي أعضاء عامة بصورة مطلقة.

قط.ال يمكن ألعضاء الواجهة أن تتضمن على شيفرة برمجية وإنما مجرد تعريف ف

.ال يمكن تعريف حقول ضمن الواجهة

ال يمكن تعريف أعضاء الواجهة باستخدام الكلمات(static,virtual,abstract,sealed).

.إن تعريف أنواع ضمن الواجهات غير مسموح به

وذلك إذا أردنا أن نحجب األعضاء الموروثة من newبينما يمكننا أن نعرف األعضاء باستخدام الكلمة

ت األساس على سبيل المثال:الواجها

interface IMyBaseInterface { void DoSomething(); } interface IMyDerivedInterface:IMyBaseInterface { new void DoSomething();

}

DoSomethingام المنهج من الواجهة المشتقة واستخد ()DoSomethingلقد قمنا هنا بحجب المنهج

المعرف ضمن الواجهة األساس.

Page 113: مستويات مبتدئ و متوسط -ج2

- 112 - إعداد: المهندس حسام الدين الرز

ويمكننا تعريف الخصائص ضمن الواجهات أيضا وبتعريفها فإننا نحدد ما إذا كانت الخاصية تتضمن

أو كلتاهما على سبيل المثال: getأو الكتلة setالكتلة

interface IMyInterface { int MyInt { get; set; }

}

وهي خاصية قراءة/كتابة ويمكننا أن IMyInterfaceضمن الواجهة MyIntلقد عرفنا هنا خاصية

فيما إذا كنا نود إنشاء خاصية للقراءة فقط أو الكتابة فقط. setأو getنحذف السطر

إنه ال يمكننا تحديد كيفية حفظ قيمة الخاصية هنا ال يمكننا كتابة شيفرة ضمن أعضاء الواجهة ف بما انه

توي على حقول.ن تحفالواجهات ال يمكنها أ

وأخيرا فإن الواجهات يمكن أن تعرف كاألصناف كأعضاء ألصناف لكن ليس كأعضاء لواجهات أخرى

وذلك باعتبار ان الواجهات ال يمكن أن تحتوي على تعريفات ألنواع.

تزويد األصناف بالواجهات:

Implementing Interface in Class:

على تزويد لجميع أعضاء تلك الواجهة بحيث يتطابق توقيع يجب على الصنف المزود بواجهة أن يحتوي

)تعريف( هذه األعضاء في الصنف مع توقيعها )تعريفها( في الواجهة باإلضافة إلى تطابق وجود الكتلتين

get وset اجهة ويجب أن تكون تلك األعضاء المزودة عامة ومن للخصائص في الصنف كما هي في الو

لكن ال يمكن استخدام الكلمات abstractأو virtualالممكن ان نزود أعضاء الواجهة باستخدام الكلمات

static أوconst :على سبيل المثال

public interface IMyInterface { void DoSomething(); void DoSomethingElse(); } public class MyClass:IMyInterface { public void IMyInterface.DoSomething () { } public void DoSomethingElse() { }

}

Page 114: مستويات مبتدئ و متوسط -ج2

- 113 - إعداد: المهندس حسام الدين الرز

ال:ويمكن ان تزود أعضاء الواجهة على أصناف أساس على سبيل المث

public interface IMyInterface { void DoSomething(); void DoSomethingElse(); } public class MyClass:IMyInterface { public void DoSomething () { } } public class MyDerivedClass:MyClass ,IMyInterface { public void DoSomethingElse() { }

}

وعندما يرث صنف ما صنف أساس مزود بواجهة ما فإن الصنف المشتق سيدعم هذه الواجهة بصورة

مطلقة على سبيل المثال:

public interface IMyInterface { void DoSomething(); void DoSomethingElse(); } public class MyBaseClass:IMyInterface { public virtual void DoSomething () { } public virtual void DoSomethingElse() { } } public class MyDerivedClass:MyBaseClass { public override void DoSomethingElse() { }

}

وكما ترى هنا فإنه من المفيد جدا أن نعرف األعضاء التي تم تزويدها من واجهة في الصنف األساس

كأعضاء ظاهرية وذلك كي تتمكن األصناف المشتقة من استبدال شيفرة هذه األعضاء بشيفرة أخرى

هذه الطريقة بدال من تجاوزه ب newمناسبة للصنف المشتق وإذا قمنا بإخفاء عضو الصنف األساس بالكلمة

سيشير دوما إلى إصدارة الصنف األساس من هذا ()IMyInterface.DoSomethingفإن المنهج

العضو.

Page 115: مستويات مبتدئ و متوسط -ج2

- 114 - إعداد: المهندس حسام الدين الرز

تزويد أعضاء الواجهة بشكل صريح:

Explicit Interface Member Implementation:

ندئذ ( بواسطة الصنف وإذا قمنا بذلك فعexplicit membersيمكن تزويد أعضاء الواجهة بشكل صريح )

يمكن الوصول إلى هذا العضو من خالل هذه الواجهة فقط وليس من خالل الصنف أما األعضاء المطلقة

(implicit members وهي األعضاء التي رأيناها في شيفرة القسم السابق قد يمكننا الوصول إليها إما )

عبر الصنف أو عبر الواجهة.

ضمن الصنف IMyInterfaceفي الواجهة ()DoSomethingعلى سبيل المثال إذا تم تزويد المنهج

MyClass :بشكل مطلق كما قمنا بلك مسبقا عندئذ فإن الشيفرة التالية صحيحة

MyClass myObj = new MyClass(); myObj.DoSomething();

تماما كالشيفرة التالية:

MyClass myObj = new MyClass(); IMyInterface myInt=myObj ;

.DoSomething();myInt

بشكل صريح فإننا لن نتمكن من ()DoSomethingبالمنهج MyDerivedClassلكن إذا زود الصنف

الوصول إلى هذا المنهج من ضمن هذا الصنف طبعا إال عند ذكر اسم الواجهة أيضا والشيفرة التالية تقوم

بشكل مطلق: ()DoSomethingElseبشكل صريح والمنهج ()DoSomethingلمنهج بتزويد ا

public class MyClass:IMyInterface { void IMyInterface.DoSomething() { } public void DoSomethingElse() { }

}

مثال تطبيقي:

Example Application:

حتى اآلن موضع OOPسوف نقوم في هذا القسم من الفصل بوضع ما تعلمناه عن البرمجة كائنية التوجه

التطبيق العملي وسوف نقوم هنا ببناء وحدة أصناف سنعتمد عليها في الفصول المقبلة تتألف وحدة أصناف

هذه من صنفين رئيسيين:

صنف ورقة اللعبCard لعب قياسية لها شكل ورقم.والذي يمثل ورقة

Page 116: مستويات مبتدئ و متوسط -ج2

- 115 - إعداد: المهندس حسام الدين الرز

صنف مجموعة ورق اللعبDeck ورقة لعب المتوفرة في مجموعة ورق 52والذي يمثل الـ

اللعب النظامية حيث يمكن الوصول إلى كل ورقة على حدة بحسب موقع الورقة من المجموعة

باإلضافة إلى إمكانية إعادة ترتيب أوراق اللعب بصورة عشوائية.

تطوير برنامج زبون بسيط الختبار وحدة الصنف تلك ولكن لن نستخدم هذه الوحدة في سوف نقوم أيضا ب

على األقل حتى اآلن()كامل! إنشاء تطبيق لعبة ورق

توضيح:

على التطبيق الذي يقوم باستضافة أو استخدام (client application)نطلق مصطلح برنامج زبون

خر أن يمثل تطبيقا غير تنفيذي وبالتالي نعتمد علىجي اآلر ويمكن لهذا التطبيق البرمتطبيق برمجي آخ

تطبيق الزبون في اختبار هذا التطبيق وفي الغالب نستخدم تطبيقات زبون لتجربة واستخدام مكتبات

األصناف.

الحظ أن بإمكاننا معاملة جميع المشاريع التي نقوم بها منذ بداية هذا الكتاب على أنها تطبيقات زبون

NET.صناف إطار عمل لمكتبات أ

التخطيط للتطبيق:

Planning the Application:

( على هذين الصنفين ولكن CardLibraryستتضمن مكتبة األصناف لهذا التطبيق )والتي سنسميها بـ

قبل أن نبدأ بإنشاء التطبيق علينا ان نخطط لبنية التطبيق والوظائف التي ستقوم بها اصنافه.

فكرة:

الخطوة األولى في إنشاء أي تطبيق هي التخطيط لما ستقوم به وكيف يجب أن تسير في الحقيقة أن

ن هذا النوع من التخطيط سيساعدك على فهم األمور ا يجب ان يحقق كل جزء من تطبيقك إاألمور وماذ

بشكل جيد ويجنبك البرمجة االعتباطية.

ورقة اللعب: صنف

The Card Class:

أية نم تتكون ورقة اللعب؟ في الحقيقة إب لنفكر في ورقة اللعب فقط مبغض النظر عن صنف ورقة اللع

ومن أحد ثالثة عشرة (Heart,Spade,Diamond,Club)ورقة لعب تمثل بواحدة من أربعة أشكال

رتبة تتراوح من اآلس إلى الملك وبالتالي لكي نحدد ورقة لعب ما فإن علينا أن نحدد شكل ورتبة الورقة.

Page 117: مستويات مبتدئ و متوسط -ج2

- 116 - إعداد: المهندس حسام الدين الرز

suitيمثل حاو لحقلين للقراءة فقط: حقل الشكل Cardورقة اللعب في الحقيقة إن الصنف لنعد إلى صنف

ن السبب الذي يجعلنا نستخدم حقوال للقراءة فقط هنا هو أنه من غير المنطقي أن إ rankوحقل الرتبة

اءها ولتبسيط شتكون لدينا ورقة لعب فارغة باإلضافة إلى أنه من غير الطبيعي أن تتغير ورقة اللعب بعد إن

األمور سنجعل منهج البناء االفتراضي لهذا الصنف منهجا خاصا وسنستخدم منهج بناء آخر يجب ان يأخذ

بارامترين: األول يمثل شكل الورقة والثاني يمثل رتبتها.

الذي يوفره الصنف األب ()ToStringسيتجاوز المنهج Classوباإلضافة على ذلك فإن الصنف

System.Object وذلك ألننا نود ان يعطي هذا المنهج نصا يدل على ورقة اللعب الحالية بدال من مجرد

نا سنستخدم نذا المنهج ولتبسيط األمور أكثر فإنص عادي يشير إلى اسم الكائن )وهو السلوك االفتراضي له

.rankو suitالتعدادات لحصر القيم التي يمكن ان يأخذها الحقالن

بالمخطط التالي: Cardة اللعب يمكننا تمثيل صنف ورق

(3-36الشكل )

صنف مجموعة أوراق اللعب:

The Deck Class:

وسوف نستخدم لذلك مصفوفة Cardكائنا من نوع صنف ورقة اللعب 52يجب أن يعالج هذا الصنف

لن نتمكن من الوصول إلى هذه المصفوفة بصورة مباشرة وإنما سنوفر منهجا باسم Cardمن نوع

GetCard() مشابه لسحب ورقة لعب من فهو لى كل ورقة لعب في المصفوفة على حدةللوصول إ

وفقا لموقع ورقة اللعب تلك من Cardمجموعة أوراق اللعب حيث سيعيد هذا المنهج كائنا من نوع

المصفوفة.

وبناء على ذلك ()Shuffleسيتضمن هذا الصنف منهجا لخلط وإعادة ترتيب أوراق اللعب وسنسميه

كما في الشكل التالي: CardLibraryيمكننا رسم تخطيط مكتبة األصناف

Page 118: مستويات مبتدئ و متوسط -ج2

- 117 - إعداد: المهندس حسام الدين الرز

(3-37الشكل )

برمجة مكتبة أصناف ورق اللعب:

Writing the Class Library

أصبحت مألوفة بالنسبة إليك يجب أن تكون Visual Studio 2013ستفترض في هذا المثال أن بيئة

ضمن خطوات بشكل صريح.كذلك لذا فإننا لن نرتب ما سنقوم به

باسم Class Libraryباإلضافة إلى تعدادين في مشروع مكتبة أصناف Deckو Cardنا يسنضع صنف

CardLibrary سيتضمن هذا المشروع ملفين باالمتداد.cs أحدهماCard.cs والذي يتضمن صنف

لذي يتضمن صنف وا Deck.csواآلخر Rankو Suitباإلضافة إلى التعدادين Cardورقة اللعب

.Deakمجموعة أوراق اللعب

:Card.csالملف

Card.cs:

وسنبدأ بالطبع مع تصريح فضاء اوذلك لشرح كل خطوة فيه Card.csسوف نقوم هنا بتجزئة شيفرة

.usingاألسماء وتعليمة

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CardLibrary

Page 119: مستويات مبتدئ و متوسط -ج2

- 118 - إعداد: المهندس حسام الدين الرز

{

:Suitبعد ذلك سنقوم بتعريف تعداد ليمثل أشكال ورقة اللعب

public enum Suit { Clup, Diamond, Heart, Spade

}

أي اآلس: 1سنبدأ بالقيمة حيث Rankثم سنعرف تعداد آخر لتمثيل رتب األوراق باسم

public enum Rank { Ace=1, Deuce, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King

}

التعدادين عامان وذلك ألننا سنستخدمهما خارج مكتبة األصناف من خالل تطبيق الزبون. الحظ أن هذين

حيث سنقوم بتعديل الوصولية Cardبعد ذلك سننتقل إلى القسم الرئيسي لهذا الملف وهو تعريف الصنف

إلى عام:

public class Card {

}

واآلخر Suitقلين للقراءة فقط أحدهما من نوع التعداد سنعرف ح Cardفي القسم األول من شيفرة الصنف

:Rankمن نوع التعداد

public readonly Suit suit; rank; Rank readonly public

حيث سنستخدم الحقلين السابقين لعرض نص يبين ورقة ()ToStringومن ثم سنقوم بتجاوز المنهج

اللعب الحالية:

public override string ToString() { //return base.ToString();

Page 120: مستويات مبتدئ و متوسط -ج2

- 119 - إعداد: المهندس حسام الدين الرز

return "The"+rank +" of"+suit+"s"; }

وبوضعنا ألسماء الحقول بهذه الطريقة سيؤدي ذلك إلى إعادة اسم ورقة اللعب وفقا لشكلها ولرتبتها على

هيئة نص مقروء بالنسبة إلينا.

بعد ذلك سنعرف منهج البناء االفتراضي للصنف وسنجعله منهجا خاصا:

private Card () {

}

ويتقبل هذا المنهج Cardسنعرف اآلن منهج بناء عام لهذا الصنف سنستخدمه إلنشاء كائنات الصنف

بارامترين األول يمثل شكل الورقة والثاني يمثل رتبتها:

public Card (Suit newSuit,Rank newRank) { suit =newSuit ; rank =newRank ;

}

هو كما يلي: Card.csوالشكل النهائي للملف Cardوبهذا نكون قد انتهينا من كتابة صنف ورقة اللعب

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CardLibrary { public enum Suit { Clup, Diamond, Heart, Spade } public enum Rank { Ace = 1, Deuce, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King

Page 121: مستويات مبتدئ و متوسط -ج2

- 120 - إعداد: المهندس حسام الدين الرز

} public class Card { public readonly Suit suit; public readonly Rank rank; public override string ToString() { //return base.ToString(); return "The "+rank +" of "+suit+"s"; } private Card () { } public Card (Suit newSuit,Rank newRank) { suit = newSuit; rank = newRank; } } }

:Deck.csالملف

Deck.cs:

كما يلي: Card.csيبدأ هذا الملف بنفس ما يبدأ به الملف

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CardLibrary {

لذا سنقوم بتعريف هذا الصنف كما يلي: Deckليست لدينا هنا أية أنواع لتعريفها سوى الصنف

public class Deck {

}

أي أن هذه Cardوأول عضو سنعرفه ضمن هذا الصنف هو مصفوفة خاصة تتضمن عناصر من نوع

:Cardsبـ وسنسمي هذه المصفوفة Cardالمصفوفة تحتوي على كائنات من نوع

[] Cards;Card private

بعد ذلك سنقوم بإنشاء منهج بناء للصنف وبما انه ليست هناك أية شروط معينة يجب القيام بها قبل إنشاء

كائنا من 52مجموعة أوراق اللعب فإننا سنستخدم منهج البناء االفتراضي لذلك سيقوم هذا المنهج بوضع

ومن ثم سنقوم بإنشاء حلقة للمرور على جميع األوراق المتوفرة Cardsضمن المصفوفة Cardالصنف

Page 122: مستويات مبتدئ و متوسط -ج2

- 121 - إعداد: المهندس حسام الدين الرز

في مجموعة أوراق اللعب ووضعها ضمن المصفوفة لكي نقوم بذلك علينا أن نشكل حلقتين واحدة ضمن

األخرى حيث ستمر الحلقة األولى على أشكال أوراق اللعب األربعة ولكل شكل من هذه األشكال سيتم

دورة لكال الحلقتين والنتيجة هي وضع 52لثالثة عشر الممكنة وهو ما مجموعه المرور على الرتب ا

بصورة مرتبة كما لو أننا قمنا بشراء مجموعة أوراق لعب جديدة!: Cardsأوراق اللعب ضمن المصفوفة

public Deck () { Cards = new Card[52]; for (int suitVal=0;suitVal <4;suitVal ++) { for (int rankVal=1;rankVal <14;rankVal ++) { Cards[suitVal * 13 + rankVal - 1] = new Card((Suit)suitVal, (Rank)rankVal); } }

}

مالحظة:

suitValفي السطر األخير وهذا واضح ألن متحولي الحلقتين cast الحظ أننا استخدمنا التشكيل

على الترتيب. Rankو Suitوبالتالي علينا أن نشكلهما للنوع intمن النوع rankValو

يمثل دليل ورقة اللعب في int والذي يأخذ بارامترا من النوع ()GetCardواآلن سوف نكتب المنهج

وإال فسيتم رمي 51و 0ا كانت قيمة البارامتر بين ذوذلك إ Cardالمصفوفة ويعيد كائنا من النوع

اعتراض بنفس الطريقة التي تعلمناها مسبقا:

public Card GetCard(int cardNum) { if (cardNum >= 0 && cardNum <= 51) return Cards[cardNum]; else throw (new System.ArgumentOutOfRangeException("cardNum", cardNum, "Value must be between 0 and 51."));

}

رتيبها بصورة عشوائية وسنسمي هذا المنهج وأخيرا سنقوم بإنشاء منهج لخلط أوراق اللعب وإعادة ت

سيقوم هذا المنهج بإنشاء مصفوفة مؤقتة نقوم فيها بنسخ أوراق اللعب من المصفوفة ()Shuffleباالسم

cards الحالية إلى المصفوفة المؤقتة بصورة عشوائية إن الجسم الرئيسي لهذا التابع عبارة عن حلقة تبدأ

باستخدام كائن من النوع 51و 0ذه الحلقة سيتم توليد رقم عشوائي بين وعند كل دورة له 51إلى 0من

System.Random يستخدم لهذا الغرض ومتى حصلنا على القيمة العشوائية فإننا سنستخدمها كدليل

( ضمن مصفوفتنا المؤقتة حيث سنضع ضمن هذا الدليل ورقة لعب من Cardلورقة اللعب )كائن

األصلية. Cardsالمصفوفة

Page 123: مستويات مبتدئ و متوسط -ج2

- 122 - إعداد: المهندس حسام الدين الرز

توضيح:

كائن من هذا بإنشاءومتى قمنا NET.طار عمل إأصناف أحد System.Randomيمثل الصنف

.Next(X)باستخدام المنهج وذلك Xوالصنف فإنه يمكننا توليد قيمة بين الصفر

ولكي نتمكن من تتبع أوراق اللعب التي تم وضعها ضمن المصفوفة المؤقتة فإننا سنستخدم مصفوفة

لكل ورقة لعب تم نسخها trueوسنعطى كل عنصر من هذه المصفوفة القيمة Boolنوع عناصرها من

ورقة اللعب التي مع دليل boolإلى المصفوفة المؤقتة حيث سيتطابق دليل القيمة في المصفوفة من النوع

لدة تمثل لمووعند توليد القيم العشوائية سنقوم بتفحص ما إذا كانت القيمة ا Cardsتم سحبها من المصفوفة

فهذا يعني أن هذا trueفإذا كانت القيمة falseأو trueدليال لعنصر في المصفوفة المنطقية يأخذ القيمة

العنصر قد تم نسخه إلى المصفوفة المؤقتة وإال فإنه سيتم نسخه إليها.

وائية قبل ام العشقد تجد أن هذه الشيفرة ليست فعالة بالدرجة الكافية فقد يتم توليد عدد كبير من األرق

#Cالوصول إلى ورقة لعب لم يتم نسخها بعد إلى المصفوفة المؤقتة ولكننا لن نلحظ ذلك ابدا ألن شيفرة

تنفذ بسرعة كبيرة بحيث لن نشعر بأي تأخر زمني لذلك.

كما يلي: ()Shuffleوشيفرة المنهج

public void Shuffle() { Card[] newDeck = new Card[52]; bool[] assigned = new bool[52]; for (int i=0;i<52;i++) { int destCard = 0; bool foundCard = false; Random sourceGen = new Random(); while (foundCard ==false ) { destCard = sourceGen.Next(52); if (assigned[destCard] == false) foundCard = true; } assigned [destCard ]=true ; newDeck[destCard] = Cards[i]; } Cards = newDeck;

}

كما يلي: Deck.csوبذلك نكون قد أنهينا هذا الصنف والشكل النهائي للملف

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CardLibrary

Page 124: مستويات مبتدئ و متوسط -ج2

- 123 - إعداد: المهندس حسام الدين الرز

{ public class Deck { private Card[] Cards; public Deck () { Cards = new Card[52]; for (int suitVal=0;suitVal <4;suitVal ++) { for (int rankVal=1;rankVal <14;rankVal ++) { Cards[suitVal * 13 + rankVal - 1] = new Card((Suit)suitVal, (Rank)rankVal); } } } public Card GetCard(int cardNum) { if (cardNum >= 0 && cardNum <= 51) return Cards[cardNum]; else throw (new System.ArgumentOutOfRangeException("cardNum", cardNum, "Value must be between 0 and 51.")); } public void Shuffle() { Card[] newDeck = new Card[52]; bool[] assigned = new bool[52]; for (int i=0;i<52;i++) { int destCard = 0; bool foundCard = false; Random sourceGen = new Random(); while (foundCard ==false ) { destCard = sourceGen.Next(52); if (assigned[destCard] == false) foundCard = true; } assigned [destCard ]=true ; newDeck[destCard] = Cards[i]; } Cards = newDeck; } } }

Page 125: مستويات مبتدئ و متوسط -ج2

- 124 - إعداد: المهندس حسام الدين الرز

تطبيق زبون لمكتبة أصناف أوراق اللعب:

A Client Application for the Class Library:

بسيط ليمثل تطبيق زبون لمكتبة أصناف أوراق اللعب سنضيف مشروع Consoleسنقوم بإنشاء تطبيق

الذي يتضمن مشروع مكتبة األصناف السابقة وللقيام بذلك تأكد من Solutionهذا التطبيق إلى الحل

ديد حيث عند إنشاء مشروع ج Solutionمن القائمة المنسدلة Add To Solution اختيار الخيار

:ConsoleCardClientسنسميه

(3-38الشكل )

الجديد هذا علينا أن ننشئ مرجعا لمكتبة األصناف Consoleولكي نستخدم مكتبة األصناف في تطبيق

ضمن التطبيق ويتم ذلك بعد إنشاء التطبيق الجديد من خالل الضغط بزر الفأرة األيمن على

ConsoleCardClient حل في نافذة مستعرض الSolution Explorer ومن ثم اختيار األمر

Reference الفرعيةمن القائمة Add صندوق حوار عندئذ يظهر للقائمة المنسدلةReference

Manager البند منSolution المتوضع على يسار صندوق الحوار نختارproject ثم نقوم بتفعيل

.okثم نضغط الزر CardLibraryصندوق التحقق امام

Page 126: مستويات مبتدئ و متوسط -ج2

- 125 - إعداد: المهندس حسام الدين الرز

(3-39الشكل )

بما أن المشروع هذا هو المشروع الثاني من الحل الحالي فإن عندئذ سيتم إضافة المرجع إلى التطبيق.

للحل مما يعني أنه سيتم تنفيذ هذا المشروع Startup Projectعلينا أن نحدد أنه يمثل مشروع بدء التنفيذ

تار بزر الفأرة األيمن على المشروع ومن ثم نخعند الضغط على زر التنفيذ وللقيام بذلك يكفي أن نضغط

.Solution Explorerمن القائمة المنسدلة في إطار Set as StartUp Projectاألمر

بعد ذلك سنقوم بإضافة الشيفرة التالية التي ستستخدم صنفينا اللذين أنشأناهما مسبقا إن هذه الشيفرة ال

بذلك:تتطلب أي شيء خاص والشيفرة التالية تقوم

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using CardLibrary; namespace ConsoleCardClient { class Program { static void Main(string[] args) { Deck myDeck = new Deck(); myDeck.Shuffle(); for (int i=0;i<52;i++) { Card tempCard = myDeck.GetCard(i); Console.Write(tempCard.ToString());

Page 127: مستويات مبتدئ و متوسط -ج2

- 126 - إعداد: المهندس حسام الدين الرز

if (i != 51) Console.Write(" , "); else Console.WriteLine(); } } } }

والنتيجة هي كما يلي:

(3-40الشكل )

بصورة غير مرتبة. Consoleحيث تم طباعة مجموعة أوراق اللعب في نافذة

األصناف هذه في الفصول الالحقة.سوف نستمر في تطوير واستخدام مكتبة

Page 128: مستويات مبتدئ و متوسط -ج2

- 127 - إعداد: المهندس حسام الدين الرز

الخالصة:

Summary:

لقد انتهينا في هذا الفصل من أساسيات تعريف األصناف ومازال هناك الكثير للمناقشة حول األصناف في

يمكننا من إنشاء تطبيقات معقدة بالصورة التي اآلنالفصول القادمة ولكن كما نالحظ فإن ما تعلمناه حتى

نتطلبها.

قد تعلمنا كيفية تعريف الحقول والمناهج والخصائص باإلضافة إلى مناقشة مستويات الوصول العديدة ل

Visualومعالجات وكيفية استخدام مقيدات الوصول مع هذه األعضاء تحدثنا بعد ذلك عن أدوات

Studio 2013 ة عالمساعدة التي تمكننا من إنشاء وتعريف األعضاء ضمن األصناف والواجهات بسر

ويسر.

رأيناوتقدما مثل سلوك وراثة األعضاء أكثروبعد أن انتهينا من المواضيع األساسية انتقلنا إلى مواضيع

وكيفية newكيفية حجب األعضاء الموروثة غير المرغوب فيها في األصناف المشتقة باستخدام الكلمة

ولقد baseوذلك باستخدام الكلمة جديد الوصول إلى أعضاء الصنف األساس بدال من استبدالها بتزويد

تعرفنا أيضا على تعريفات األصناف المعششة.

ثم انتقلنا من األصناف إلى الواجهات وكيفية تعريفها وتزويد األصناف بها باإلضافة على مفاهيم التزويد

المطلق والتزويد الصريح للواجهات.

ة أوراق اللعب وسوف نحدث ونستخدم هذه وأخيرا فقد قمنا بتطوير واستخدام مكتبة أصناف تمثل مجموع

المكتبة في الفصول الالحقة أيضا.

Page 129: مستويات مبتدئ و متوسط -ج2

- 128 - إعداد: المهندس حسام الدين الرز

الفصل الرابع

املزيد عن األصناف

إال أن هناك بعض #Cفي لغة البرمجة OOPلقد تناولنا حتى اآلن كافة أساسيات البرمجة كائنية التوجه

سوف نتناول في هذا #Cلبدء ببناء تطبيقات في التقنيات المتقدمة والتي يجب ان نتعلمها قبل االستعداد ل

الفصل التقنيات التالية:

المجموعات(Collections): يمكن أن تحوي الكائنات على مصفوفات لكائنات أخرى بحيث

تتضمن مصفوفات الكائنات تلك وظائفا للتحكم بالوصول إلى هذه الكائنات وتسمى هذه اآللية

بالمجموعات.

واملالتحميل الزائد للع(Operator overloading) : ويعرف بأنه إعداد معين لألصناف

مع كائنات الصنف. –يمكننا من استخدام عوامل مثل عامل الجمع + أو الطرح

النسخ العميق(Deep Copying): أي نسخ كائن إلى كائن آخر جديد بصورة كاملة )دون

ي الكائن المنسوخ(.وجود أية مرجعية للبيانات المخزنة ضمن الكائن المصدر ف

االعتراضات المخصصة(Custom exceptions) : حيث سنتعلم كيفية إنشاء اعتراضاتنا

الخاصة بنا بتوفير معلومات إضافية للشيفرة التي ستصطاد االعتراض.

المجموعات:

Collections:

يمكنه اء متحولكيفية استخدام المصفوفات إلنش للكتاب من الجزء األوللقد رأينا في الفصل الخامس

استيعاب عدد من المتحوالت التي لها نوع محدد إن للمصفوفات نقاط سلبية عديدة وأهم تلك السلبيات هي

أن لها حجما ثابتا فمتى قمنا بإنشاء المصفوفة لن نتمكن بعدها من تغيير حجمها إن هذا يعني أن الشيفرة

من إنشاء أصناف تقوم بمهمة OOPننا تقنية تمك .ستعالج المصفوفات ستصبح معقدة لدرجة كبيرةالتي

دارة العناصر ضمن المصفوفة وبذلك تصبح الشيفرة التي تستخدم الئحة العناصر أو المصفوفة تلك إ

بسيطة وغير معقدة.

المصفوفات إال أنها ديناميكية أي أنها إنها تشبهوقلنا Listكما تعرفنا في الفصل الخامس على اللوائح

ابقا بأنها أصناف ها سليفي الحقيقة تعرف المصفوفات واللوائح التي تعرفنا عتهيئة مسبقا ليست بحاجة لل

Page 130: مستويات مبتدئ و متوسط -ج2

- 129 - إعداد: المهندس حسام الدين الرز

Generic Collections أي انها موجودة ضمن فضاء األسماءSystem.Collections.Generic

كما أنها تنتمي لفضاء األسماء non-Genericحدث عنها االن فهي توصف بأنها تأما المجموعات التي سن

System.Collections .وتتميز هذه المجموعات بأنها تملك خصائص المصفوفات واللوائح معا

تستخدم أصناف المجموعة بصورة عامة لمعالجة الئحة من الكائنات ويمكن أن تحتوي على #Cفي

وظائف إضافية بالمقارنة مع المصفوفات االعتيادية ويتم الوصول إلى هذه الوظائف من خالل التزود

يتضمن فضاء األسماء بعض األمور المثيرة System.Collectionsجهات من فضاء األسماء بوا

األخرى أيضا.

يمكننا الوصول إلى عناصر المجموعة من خالل دليل العنصر كما في المصفوفات باإلضافة إلى إمكانية

عات ليست الوصول إلى العناصر من خالل أشياء أخرى فوظائف الوصول إلى العناصر ضمن المجمو

وإنما يمكننا إنشاء أصناف System.Arrayمحددة باستخدام أصناف المجموعة األساسية كما في النوع

خص كائنات من نوع محدد بحيث تتضمن على ات تمجموعة مخصصة لنا وبالتالي يمكننا إنشاء مجموع

وظائف خاصة لالئحة هذه الكائنات.

تي تعرف والالبنى اف وى عدد من الواجهات واألصنعل System.Collectionsفضاء األسماء يحتوي

وظائف المجموعة األساسية.

:System.Collectionsوالجدول التالي يبين أهم األصناف التي يحتويها فضاء األسماء

الوصف الفئة

ArrayList التي تمكن من إنشاء مصفوفة متغيرة الحجم IListيرث هذا الصنف الواجهة

.فة إلى إمكانيات الحذف واإلضافة( باإلضا)ديناميكية

BitArray

هذا الصنف يمكننا من التحكم بمصفوفة من البتات عن طريق إرجاعها لقيم

بالقيمة المنطقية 0و القيم Trueبالقيمة المنطقية 1منطقية حيث تمثل القيمة

False

Comparer .يقوم هذا الصنف بالمقارنة بين كائنين

CollectionBase نف مجرد أساسي يزودنا بمناهج قوية للمجموعات. وهو ص

Hashtable .يعرف هذا الصنف بأنه مجموعة من المفاتيح المرتبطة يشار أليها بدليلها

Queue الداخلين هو اول لهذا الصنف يعرف باسم الطابور ويعمل على مبدأ أو

الخارجين

SortedList على المفاتيح واألدلة. هذا الصنف يقوم بترتيب عناصر المجموعة بناء

Stack هذا الصنف يشبه صنف الطابور إال أن العنصر األخير في الدخول هو العنصر

األول في الخروج.

:System.Collectionsوالجدول التالي يبين اهم الواجهات التي يحتويها فضاء األسماء

الوصف الواجهة

ICollection مجموعة وإمكانية نسخ وتمكننا من إيجاد عدد العناصر في ال

العناصر إلى مصفوفة عادية.

Page 131: مستويات مبتدئ و متوسط -ج2

- 130 - إعداد: المهندس حسام الدين الرز

IComparer .تمكننا من المقارنة بين كائنين

IDictionary

تمكننا من الوصول إلى عناصر الالئحة عبر قيمة مفتاحية محددة

بدال من مجرد استخدام دليل العنصر ضمن الالئحة كما في

المصفوفات التقليدية

IEnumerable كانية المرور خالل العناصر في المجموعات.توفر هذه إم

IEnumerator تدعم تكرار بسيط للمجموعة

IList وتوفر الئحة من العناصر تتضمن إمكانية للوصول إلى هذه

العناصر باإلضافة إلى إمكانية مرتبطة بلوائح العناصر.

IDictionaryEnumerator .يعدد عناصر المجموعة عبر قيمة مفتاحية

وهذا IEnumerableو ICollectionو IListبالواجهات System.Collectionsد الصنف يزو

واضح ألن جميع الوظائف التي تقدمها هذه الواجهات الثالثة متوفرة في المصفوفات التقليدية إال أنها ال

ثابت. باإلضافة إلى أنها تمثل الئحة العناصر بحجم IListتدعم بعضا من المزايا المتقدمة للواجهة

استخدام المجموعات:

Using Collections:

System.Collections.ArrayListعلى الصنف System.Collectionsيحتوي فضاء األسماء

إال انه يحتوي على وظائف إضافية IEnumerableو ICollectionو IListوهو مزود بالواجهات

إنشاء مصفوفات بأحجام متغيرة إن المثال فهذا الصنف يمكننا من System.Arrayبالمقارنة مع الصنف

أفضل طريقة لفهم األمور لذا إليك التطبيق التالي الذي يبين لنا كيفية استخدام المجموعات.

تطبيق حول المصفوفات مقابل المجموعات األكثر تقدما:

.ConsoleCollectionsجديد باسم Consoleقم بإنشاء تطبيق -1

وذلك باستخدام Animal.csإلى المشروع في ملف مستقل Animalأضف صنفا جديدا باسم -2

المعالج المساعد.

كما يلي: Animal.csعدل الشيفرة في الملف -3

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleCollections { public abstract class Animal

Page 132: مستويات مبتدئ و متوسط -ج2

- 131 - إعداد: المهندس حسام الدين الرز

{ protected string name; public string Name { get { return name; } set { name = value; } } public Animal () { name = "The animal with no name"; } public Animal (string newName) { name = newName; } public void Feed() { Console.WriteLine("{0} has been fed.", name); } } public class Cow:Animal { public void Milk() { Console.WriteLine("{0} has been milked.", name); } public Cow (string newName):base(newName ) { } } public class Chicken:Animal { public void LayEgg() { Console.WriteLine("{0} has laid an egg.", name); } public Chicken (string newName):base(newName ) { } } }

كما يلي: csogramPr.عدل الشيفرة في الملف -4

using System; using System.Collections.Generic; using System.Linq; using System.Text;

Page 133: مستويات مبتدئ و متوسط -ج2

- 132 - إعداد: المهندس حسام الدين الرز

using System.Threading.Tasks; using System.Collections; namespace ConsoleCollections { class Program { static void Main(string[] args) { Console.WriteLine("Creat an Array type collection of" + "Animal objects and use it:"); Animal[] animalArray = new Animal[2]; Cow myCow = new Cow("Deirdre"); animalArray[0] = myCow; animalArray[1] = new Chicken("Ken"); foreach (Animal myAnimal in animalArray ) { Console.WriteLine("New {0} object added to Array" + "collection,Name={1}",myAnimal .ToString (), myAnimal .Name ); } Console.WriteLine("Array collection contains {0} objects>", animalArray.Length); animalArray[0].Feed(); ((Chicken)animalArray[1]).LayEgg(); Console.WriteLine(); Console.WriteLine("Create an ArrayList type collection"+ "of Animal object and use it:"); ArrayList animalArrayList = new ArrayList(); Cow myCow1 = new Cow("Hayley"); animalArrayList.Add(myCow1); animalArrayList.Add(new Chicken("Roy")); foreach (Animal myAnimal in animalArrayList ) { Console.WriteLine("New {0} object added to ArrayList" + "collection,Name={1}", myAnimal.ToString(), myAnimal.Name); } Console.WriteLine("ArrayList collection contains {0}" + "objects.", animalArrayList.Count); ((Animal)animalArrayList[0]).Feed(); ((Chicken)animalArrayList[1]).LayEgg(); Console.WriteLine(); Console.WriteLine("Additional manipulation of ArrayList:"); animalArrayList.RemoveAt(0); ((Animal)animalArrayList[0]).Feed(); animalArrayList.AddRange(animalArray); ((Chicken)animalArrayList[2]).LayEgg(); Console.WriteLine("The animal called {0} is at index {1}.", myCow .Name ,animalArrayList .IndexOf (myCow )); myCow.Name = "Janice"; Console.WriteLine("The animal is now called {0}.", ((Animal)animalArrayList[1]).Name); Console.ReadLine();

Page 134: مستويات مبتدئ و متوسط -ج2

- 133 - إعداد: المهندس حسام الدين الرز

} } }

.F5نفذ التطبيق بالضغط على مفتاح -5

(4-1الشكل )

كيفية العمل:

How To Work:

أي System.Arrayبإنشاء مجموعتين من الكائنات األولى باستخدام الصنف في هذا المثال القد قمن

حيث تحتوي كلتا System.Collections.ArrayListوالثانية باستخدام الصنف تقليديةمصفوفة

والذي تم تعريفه ضمن Animalأي كائنات من النوع Animalالمجموعتين على الئحة من كائنات

هو صنف مجرد وبالتالي ال يمكننا إنشاء كائنات منه مباشرة Animalنف إن الص Animal.csالملف

Cowولكن باستخدام تعددية األشكال يمكن أن يكون لدينا عناصر ضمن المجموعتين من النوع

.Animalأصنافا مشتقة من الصنف باعتبارها Chickenو

ستتم Program.csالملف موجود ضمن ()Mainومتى قمنا بإنشاء هاتين المجموعتين في المنهج

معالجة هاتين المجموعتين الستعراض صفاتهما وإمكانياتهما إن معظم العمليات المستخدمة هنا يمكن

على الرغم من أن لكل منهما ArrayListومجموعة Arrayتطبيقها على كال المجموعتين أي مجموعة

ن استخدامها مع المجموعات من نوع صيغ مختلفة للقيام بذلك إال أن هناك بعض العمليات التي يمك

ArrayList .فقط

لنتناول العمليات المتشابهة في كلتا المجموعتين لنقارن الشيفرة والنتائج لنوعي المجموعتين.

Page 135: مستويات مبتدئ و متوسط -ج2

- 134 - إعداد: المهندس حسام الدين الرز

أوال إنشاء المجموعة: فبالنسبة للمصفوفات التقليدية علينا تهيئة المصفوفة بحجم ثابت لكي نتمكن من

باستخدام الصيغة القياسية التي تعلمناها animalArrayستخدام مصفوفة باسم استخدامها ولقد قمنا بذلك با

في الفصل الخامس:

[2];Animal new[] animalArray = Animal

فإنها ال تحتاج إلى تحديد حجم لها أثناء تهيئتها لذا يمكننا ArrayListأما بالنسبة للمجموعات من الصنف

بسهولة كما يلي: animalArrayListتسمى في شيفرتنا بـ إنشاء المجموعة والتي

();ArrayList newanimalArrayList = ArrayList

يقوم المنهج ArrayListبناء آخران يمكننا استخدامهما عند إنشاء مجموعات من النوع وهناك منهجا

تركبارامبتحديد المجموعة المراد نسخها األول بنسخ مجموعة موجودة مسبقا إلى المجموعة الجديدة وذلك

ارامتربخر فهو يقوم بتحديد سعة المجموعة وذلك أيضا عبر تمرير في منهج البناء وأما منهج البناء اآل

حيث تحدد العدد األول للعناصر التي يمكن أن تتضمنها intيمثل ذلك والسعة عبارة عن قيمة من النوع

يمة ليست بالسعة الحقيقة حيث سيتم مضاعفة هذه القيمة تلقائيا متى تجاوز المجموعة في الحقيقة إن هذه الق

عدد العناصر في المجموعة هذه القيمة.

أو Animalإن تهيئة المصفوفات ذات األنواع المرجعية مثل المصفوفات التي تحتوي كائنات من نوع

ها ولكي نتمكن من استخدام مدخل ال تعني تهيئة العناصر التي ستحتوي Animalكائنات مشتقة من النوع

)المدخل هو مكان وضع العنصر في المصفوفة( في المصفوفة علينا أن نهيئ هذا المدخل أيضا مما يعني

أن علينا إسناد كائنات مهيئة مسبقا إليه وذلك كي يمثل عنصرا فعاال في المصفوفة:

Cow myCow = new Cow("Deirdre"); animalArray[0] = myCow;

);"Ken"(Chicken newanimalArray[1] =

myCow وهو-مسبقالقد قمنا بوضع الكائنات في المصفوفة بطريقتين هنا األولى بإسناد كائن موجود

في حالتنا هنا مباشرة إن الفرق هنا هو Chickenوالثانية بإسناد كائن من الصنف وهو الصنف المشتق

لحالة األولى تترك لنا مرجعا للكائن الذي أسندناه في المصفوفة الستخدامه الحقا في الشيفرة.أن ا

فليست هناك أية عناصر موجودة مسبقا وال حتى عناصر فارغة أي ArrayListأما بالنسبة لمجموعات

إسناد كائنات وبالتالي ال توجد هناك مداخل كما في المصفوفات وهذا يعني أنه ال يمكننا Nullكائنات

جديدة إلى مواقع محددة ضمن المجموعات باستخدام األدلة كما نقوم بذلك مع المصفوفات وبدال من ذلك

إلضافة عناصر جديدة كما يلي: ()Addفإننا نستخدم المنهج

Cow myCow1 = new Cow("Hayley"); animalArrayList.Add(myCow1);

));"Roy"(Chicken newanimalArrayList.Add(

والمصفوفات فإنه يمكننا هنا أيضا ArrayListصيغة العناصر بين مجموعات وبعيدا عن االختالف في

إضافة كائنات جديدة أو موجودة مسبقا إليها كما في المصفوفات.

Page 136: مستويات مبتدئ و متوسط -ج2

- 135 - إعداد: المهندس حسام الدين الرز

يغة صر واستبدالها باستخدام صومتى قمنا بإضافة العناصر بهذه الطريقة يمكننا عندئذ تجاوز هذه العنا

مطابقة لما نستخدمه مع المصفوفات:

);"Alma"(Cow newanimalArrayList[0]=

لكننا لم نقم بذلك في شيفرة تطبيقنا هذا.

للمرور عبر عناصر Foreachلقد رأينا في الفصل الخامس من الجزء األول للكتاب كيفية استخدام الحلقة

مزود بالواجهة System.Arrayك ممكن بالنسبة للمصفوفة هنا باعتبار أن الصنف المصفوفة إن ذل

IEnumerable والمنهج الوحيد في هذه الواجهة وهوGetEnumerator() يمكننا من المرور خالل

بة أكبر الحقا. الحظ اننا قمنا في شيفرتنا هنا بكتاالمصفوفة سوف نتناول ذلك بتفصيل العناصر في

في المصفوفة باستخدام هذه الحلقة: Animalن كل كائن معلومات ع

foreach (Animal myAnimal in animalArray ) { Console.WriteLine("New {0} object added to Array" + "collection,Name={1}",myAnimal .ToString (), myAnimal .Name );

}

foreachوبالتالي يمكننا أن نستخدم حلقة IEnumerableمزود بالواجهة ArrayListوالصنف

والصيغة المستخدمة هي نفسها مع المصفوفات التقليدية:

foreach (Animal myAnimal in animalArrayList ) { Console.WriteLine("New {0} object added to ArrayList" + "collection,Name={1}", myAnimal.ToString(), myAnimal.Name);

}

للمصفوفة لطباعة العناصر ضمنها على الشاشة: Lengthسنستخدم بعد ذلك الخاصية

Console.WriteLine("Array collection contains {0} objects>", animalArray.Length);

بدال من Countفيما عدا أننا سنستخدم الخاصية ArrayListويمكننا تحقيق األمر نفسه مع مجموعة

Length مع المصفوفات والتي تمثل جزء من الواجهةICollection:

Console.WriteLine("ArrayList collection contains {0}" + , animalArrayList.Count);"objects."

إن المجموعات سواء كانت مصفوفات بسيطة أو مجموعات معقدة لن تصبح مفيدة إال إذا تمكنا من

وهذا يعني Strongly typedبصورة تامة الوصول إلى العناصر التي تحتويها إن المصفوفات مصنفة

أنها تسمح بالوصول المباشر إلى األنواع التي تحتويها وهذا يعني أنه يمكننا الوصول إلى أعضاء

عناصرها بصورة مباشرة:

animalArray[0].Feed();

Page 137: مستويات مبتدئ و متوسط -ج2

- 136 - إعداد: المهندس حسام الدين الرز

المناهج المزودة من وبالتالي فإنه ال يمكننا استدعاء Animalإن نوع المصفوفة هنا هو الصنف المجرد

:Castاألصناف المشتقة مباشرة لذا فإن علينا استخدام التشكيل

)animalArray[1]).LayEgg();Chicken((

Animalولقد قمنا بإسناد كائنات System.Objectهي مجموعة من كائنات ArrayListإن مجموعة

مع جميع العناصر: castي أن علينا استخدام التشكيل إلى هذه المجموعة عبر تعددية األشكال وهذا يعن

((Animal)animalArrayList[0]).Feed(); )animalArrayList[1]).LayEgg();Chicken((

لقد استعرضنا في شيفرتنا حتى اآلن اإلمكانيات التي يقدمها نوعين من المجموعات: مجموعات

System.Array ل في جوهرها المصفوفات التقليدية ومجموعات والتي تمثSystem.ArrayList

والتي ArrayListوالشيفرة المتبقية من هذا التطبيق تستعرض بعضا من المزايا المتوفرة في مجموعات

تتفوق فيها على المصفوفات التقليدية.

()RemoveAtأو ()Removeباستخدام المنهج ArrayListأوال يمكننا إزالة العناصر من مجموعة

يقوم هذان المنهجان بإزالة ArrayListالمزود به الصنف IListواللذين يمثالن جزءا من الواجهة

العناصر من المجموعة باالعتماد على مرجع العنصر أو وفقا لدليله من المجموعة ولقد استخدمنا في

Nameوالذي له الخاصية Cowن مثالنا هذا طريقة إزالة العنصر بواسطة دليله حيث قمنا بإزالة كائ

:Hayleyالتي تحمل القيمة

animalArrayList.RemoveAt(0);

ويمكننا ان نحذف العنصر نفسه وذلك كما يلي:

animalArrayList.Remove(myCow1);

عبر كائن موجود الحظ أن لدينا مرجعا محليا لهذا الكائن حيث أننا أضفنا هذا العنصر إلى المجموعة

مسبقا بدال من إنشاء كائن جديد للقيام بذلك.

والذي قمنا Chickenوسواء استخدمنا أي من الطريقتين فإن العنصر المتبقي في المجموعة هو كائن

كما يلي: ()Feedباستدعاء أحد أعضاءه وهو المنهج

)animalArrayList[0]).Feed();Animal((

قد ال تبقى في نفس موقعها األصلي عند إزالة عنصر منها ArrayListاصر في مجموعة الحظ أن العن

من المجموعة سيؤدي إلى إزاحة العناصر األخرى 0فعلى سبيل المثال عند إزالة العنصر ذو الدليل

بب سالمتبقية موقعا واحدا من الالئحة وذلك لملء الموقع الفارغ الناتج عن العنصر المحذوف وهذا يعلل

على الرغم من أن الدليل األصلي 0في سطر الشيفرة السابق باستخدام الدليل Chickenوصولنا إلى كائن

وبالتالي سيتم رمي اعتراض إذا قمنا بكتابة 0وذلك قبل أن نحذف العنصر ذو الدليل 1لهذا الكائن هو

شيفرة كالتالية بدال من السطر السابق:

ArrayList[1]).Feed();)animalAnimal((

Page 138: مستويات مبتدئ و متوسط -ج2

- 137 - إعداد: المهندس حسام الدين الرز

توضيح:

ArrayListن هذا يعني انه من غير المنطقي في كثير من األحيان االعتماد على األدلة في مجموعات إ

عمليات حذف للعناصر من المجموعة فإن هذا سيؤدي تك تقوم بللوصول إلى العناصر فإذا كانت شيفر

ؤدي بالطبع إلى تغير أدلتها إال إذا كنت ستحذف حتما إلى تغيير مواقع العناصر في المجموعة والذي سي

العنصر األخير دوما من المجموعة عندئذ لن تكون هناك أية عمليات إزاحة للعناصر.

واحدة إلى المجموعة وذلك باستخدام دفعة من إضافة عناصر عديدة ArrayListوتمكننا مجموعات

وبالتالي فإن المصفوفة ICollectionلواجهة يتقبل هذا المنهج أي كائن مزود با ()AddRangeالمنهج

التي أنشأناها مسبقا يمكن أن تمثل بارامترا لهذا المنهج: animalArrayالتقليدية

animalArrayList.AddRange(animalArray);

animalArrayولكي نتمكن من أن هذا المنهج قام بإضافة جميع العناصر الموجودة ضمن المصفوفة

فإننا سنحاول الوصول إلى العنصر الثالث في المجموعة والذي animalArrayListلى المجموعة إ

:animalArrayيقودنا إلى العنصر الثاني من المصفوفة

)animalArrayList[2]).LayEgg();Chicken((

مالحظة:

لمجموعة إلى ا animalArrayالحظ اننا لم نقم هنا بنسخ الكائن من المصفوفة

animalArrayList وإنما ما يحدث هنا عبارة عن إنشاء مرجع لإلشارة إلى نفس كائنChicken

يشير إلى animalArrayListالذي تشير له المصفوفة هذا يعني أن العنصر الثالث من المجموعة

.animalArrayنفس الكائن الذي يشير له العنصر الثاني من المصفوفة

وإنما هذا المنهج ArrayListجزء من أي واجهات يدعمها الصنف ()AddRangeال يمثل المنهج

فقط ويستعرض حقيقة إمكانية تحديد سلوك مخصص ألصناف مجموعاتنا ArrayListخاص للصنف

أكثر مما تقدمه الواجهات لنا يحتوي هذا الصنف في الواقع على مناهج أخرى مثير لالهتمام مثل المنهج

InsertRange() ك إلضافة مصفوفة من الكائنات إلى أي موضع من المجموعة وهناك مناهج لمهام وذل

أخرى كفرز العناصر وإعادة ترتيبها.

وأخيرا فإننا نعود إلى حقيقة أنه يمكن أن يكون لدينا مراجع عديدة إلى الكائن نفسه فباستخدام المنهج

IndexOf() والذي يمثل جزءا من الواجهةIList نرى أن الكائن ال يمكننا أنmyCow وهو كائن

أصبح يمثل جزءا من المجموعة animalArrayأصلي تمت إضافته إلى المصفوفة

animalArrayList :وحسب وإنما يمكننا أيضا إيجاد دليل الكائن في المجموعة

Console.WriteLine("The animal called {0} is at index {1}.", yCow .Name ,animalArrayList .IndexOf (myCow ));m

Page 139: مستويات مبتدئ و متوسط -ج2

- 138 - إعداد: المهندس حسام الدين الرز

وكامتداد لذلك فإن السطرين التاليين للسطر السابق بقومان بتغيير اسم الكائن عبر مرجعه ومن ثم عرض

االسم الجديد من خالل مرجع الكائن في المجموعة:

myCow.Name = "Janice"; Console.WriteLine("The animal is now called {0}.",

)animalArrayList[1]).Name);Animal((

تعريف المجموعات:

Defining Collections:

لقد رأينا للتو ما يمكننا القيام به باستخدام أصناف مجموعات تحوي مزايا متقدمة لقد حان الوقت اآلن لكي

ة بنا عن أحد الطرق للقيام بذلك هو بتزويد صنف مجموعتنا نرى كيف يمكننا إنشاء مجموعاتنا الخاص

بالمناهج الضرورية لمعالجة المجموعة وبكتابة شيفرة هذه المناهج يدويا إال أن هذه الطريقة مرهقة لنا

ل بديل لذلك اشتقاق مجموعتنا من صنف مثل الصنف كحدة جدا مع بعض المناهج ويمكننا وقد تكون معق

System.Collections.CollectionBase والذي يمثل صنفا مجردا يوفر التزويدات التي نحتاجها

لمعالجة المجموعات وذلك هو الخيار األفضل طبعا.

إال أنه يقدم IList و ICollectionو IEnumerableمزود بالواجهات CollectionBaseإن الصنف

()RemoveAtوالمنهج ()Clearالشيفرة األساسية المطلوبة ألعضاء هذه الواجهات مثل المنهج

وبالتالي فإننا نحتاج إلى التزود بجميع األشياء ICollectionللواجهة Countوالخاصية IListللواجهة

األخرى بأنفسنا إذا أردنا توفير وظائف أخرى لمجموعتنا.

ى خاصيتين محميتين تمكننا من الوصول إل CollectionBaseلكي نبسط األمور عليك يوفر الصنف

والتي تمكننا من الوصول إلى Listالكائنات المخزنة ضمن المجموعة يمكننا أن نستخدم الخاصية

المستخدم لحفظ ArrayListوالتي تمثل InnerListأو الخاصية IListالعناصر من خالل الواجهة

العناصر.

ضمنها Animalنوع على سبيل المثال يمكننا تعريف األشياء األساسية لصنف مجموعة لحفظ كائنات من

شيفرة هذه التعريفات الحقا:كما يلي سوف نتناول

public class Animals:CollectionBase { public void Add(Animal newAnimal) { List.Add(newAnimal); } public void Remove(Animal oldAnimal) { List.Remove(oldAnimal); } public Animals () { }

Page 140: مستويات مبتدئ و متوسط -ج2

- 139 - إعداد: المهندس حسام الدين الرز

}

بصورة صريحة لتقبل الكائنات من نوع ()Removeو ()Addلقد قمنا هنا بكتابة شيفرة المنهجين

Animals وهما يستخدمان المنهجينAdd() وRemove() األصليين للواجهةIList ويعمل المنهجان

وذلك بعكس Animalsفقط أو مع أنواع مشتقة من النوع Animalsالسابقان مع الكائنات من نوع

الذي رأيناه مسبقا والذي يمكن أن يتقبل أي كائن من أي نوع. ArrayListالصنف

مكننا أيضا مع مجموعتنا وي foreachمن استخدام الحلقة CollectionBaseيمكننا الصنف األساس

على سبيل المثال كتابة شيفرة كما يلي:

Console.WriteLine("Using custom collection class Animals:"); Animals animalCollection = new Animals(); animalCollection.Add(new Cow("Sarah")); foreach (Animal myAnimal in animalCollection ) { Console.WriteLine("New {0} object added to custom collection" + "Name={1}", myAnimal.ToString(), myAnimal.Name);

}

ولكن ال يمكننا على سبيل المثال كتابة شيفرة كما يلي:

animalCollection[0].Feed();

والسبب في ذلك أنه ال يمكننا الوصول إلى العناصر عبر أدلتها ولكي نتمكن من ذلك يجب أن نستخدم ما

.(indexers)يعرف بالمفهرسات

المفهرسات:

Indexers:

لمجموعة ا يمثل المفهرس خاصية من نوع خاص يمكننا إضافتها إلى الصنف لتوفير وصول إلى عناصر

بشكل مشابه ألسلوب الوصول إلى العناصر في المصفوفات في الحقيقة تمكننا هذه الخاصية من الوصول

إلى العناصر بصورة أكثر تعقيدا وذلك باعتبار أنه يمكننا استخدام بارامترات معقدة ضمن قوسي دليل

صول ن استخدام قيمة رقمية للوإفالعنصر في المجموعة بالطريقة التي نريد وكما الحظنا مع المصفوفات

إلى العنصر في المجموعة هو أبسط أنواع المفهرسات.

كما يلي: Animalلكائنات Animalsيمكننا إضافة مفهرس إلى المجموعة

public class Animals : CollectionBase { public Animal this[int animalIndex] { get { return (Animal)List[animalIndex]; }

Page 141: مستويات مبتدئ و متوسط -ج2

- 140 - إعداد: المهندس حسام الدين الرز

set { List[animalIndex] = value; } } }

وفيما عدا ذلك فهي مثل أية خاصية أخرى إن "[]"مع بارامتر ضمن القوسين thisكلمة استخدمنا هنا ال

هذه الصيغة منطقية وذلك ألننا سنصل إلى المفهرس باستخدام اسم الكائن متبوعا ببارامتر الدليل ضمن

.MyAnimals[0]عبى سبيل المثال "[]"القوسين

التي توفر لنا الوصول إلى IListبواسطة الواجهة Listتستخدم هذه الشيفرة مفهرسا على الخاصية

التي تحتفظ بعناصر مجموعتنا: CollectionBaseفي الصنف ArrayListمجموعات

return (Animal)List [animalIndex];

إن System.Objectتعيد قيمة من نوع IList.Listالتشكيل الصريح مطلوب هنا باعتبار أن الخاصية

ان ننوه عنه هنا هو تعريف نوع للمفهرس فهذا هو النوع الذي سنحصل عليه عند الوصول إلى ما يجب

عنصر ما باستخدام هذا المفهرس هذا يعني أنه يمكننا كتابة شيفرة كما يلي:

animalCollection[0].Feed();

بدال من:

)animalCollection[0]).Feed();Animal((

:Animalsحول استخدام المجموعة تطبيق

.ConsoleCollectionAnimalsجديد باسم Consoleقم بإنشاء تطبيق -1

Addومن ثم اختر Solution Explorerانقر بزر الفارة األيمن على اسم المشروع في إطار -2

من القائمة المنسدلة. Add Existing Itemثم

(4-2الشكل )

Page 142: مستويات مبتدئ و متوسط -ج2

- 141 - إعداد: المهندس حسام الدين الرز

:Addثم انقر على زر ConsoleCollectionsمشروع من مجلد Animal.csاختر الملف -3

(4-3الشكل )

على التالي: Animal.csعدل فضاء تعريف األسماء في الملف -4

namespace ConsoleCollectionAnimals {

ومن ثم احفظ هذا الصنف Animalsأضف صنفا جديدا باستخدام معالج إضافة األصناف باسم -5

.Animals.csضمن الملف

كما يلي: Animals.csشيفرة الصنف عدل -6

public class Animals : CollectionBase { public void Add(Animal newAnimal) { List.Add(newAnimal); } public void Remove(Animal oldAnimal) { List.Remove(oldAnimal); } public Animals() { } public Animal this[int animalIndex] { get { return (Animal)List[animalIndex]; }

Page 143: مستويات مبتدئ و متوسط -ج2

- 142 - إعداد: المهندس حسام الدين الرز

set { List[animalIndex] = value; } } }

كما يلي: Program.csعدل شيفرة الصنف -7

class Program { static void Main(string[] args) { Animals animalcollection = new Animals(); animalcollection.Add(new Cow("Jack")); animalcollection.Add(new Chicken("Vera")); foreach (Animal myAnimal in animalcollection ) { myAnimal.Feed(); } Console.ReadLine(); } }

نفذ الشيفرة: -8

(4-4الشكل )

كيفية العمل:

How To Work:

Animalsيستخدم هذا المثال الشيفرة التي تعرضنا لها في القسم األخير وذلك إلنشاء مجموعة من النوع

يقوم المنهج او من أنواع مشتقة من هذا النوع Animalوالتي ال يمكن أن تحوي إال كائنات من النوع

Main() هنا بإنشاء حالة من مجموعةAnimals في المتحولanimalCollection ومن ثم يقوم

( وهي أنواع مشتقة من الصنف Chickenوآخر من الصنف cowبإضافة عنصرين )كائن من الصنف

Animals ويستخدم بعد ذلك حلقةforeach الستدعاء المنهجFeed() الذي يرثه كائنيcow و

Chicken من الصنفAnimal.

Page 144: مستويات مبتدئ و متوسط -ج2

- 143 - إعداد: المهندس حسام الدين الرز

:IDictionaryالمجموعات المزودة بالمفاتيح والواجهة

Keyed Collections and IDictionary:

IDictionaryالتي سبق واستخدمناها أال وهي الواجهة IListيمكننا استخدام واجهة أخرى غير الواجهة

ضمن المجموعة بواسطة قيمة رقمية فريدة أطلقنا تمكننا من فهرسة العناصر IListلقد رأينا أن الواجهة

لكن ماذا لو أن مواقع )أدلة( العناصر ضمن المجموعة معرضة للتغيير بشكل indexعليها اسم الدليل

مستمر عندئذ لن نتمكن من الوصول إلى العنصر ضمن المجموعة من خالل دليله بسهولة لهذا السبب

كننا من الوصول إلى العناصر ضمن المجموعة من خالل قيم والتي تم IDictionaryوجدت الواجهة

مفتاحية فريدة تعطى لكل عنصر فيها كسلسلة نصية مثال.

يمكننا تحقيق ذلك من خالل المفهرسات أيضا إال أن البارامتر المستخدم ضمن المفهرس يمثل المفتاح

دليل ذلك العنصر بارامتر من المرتبط بالعنصر الموجود ضمن المجموعة وذلك بدال من مجرد استخدام

.intنوع

وكما في المجموعات المفهرسة فإن هناك صنف أساس يمكننا استخدامه لتبسيط التزود بالواجهة

IDictionary أال وهو الصنفDictionaryBase إن هذا الصنف مزود أيضا بالواجهتين

IEnumerable وICollection لمعالجة أي مجموعة.توفران اإلمكانيات األساسية واللتان

حيث أنه مزود ببعض أعضاء الواجهات CollectionBaseمشابه للصنف DictionaryBaseالصنف

ولكن Countوالخاصية ()Clearفهو يتضمن المنهج CollectionBaseالتي يدعمها فكما في الصنف

IListالواجهة موجود ضمن ()RemoveAtإن هذا بسبب أن المنهج ()RemoveAtال يستخدم المنهج

تحتوي على IDictionaryعليه ولكن بالمقابل فإن الواجهة IDictionaryفقط وال تحتوي الواجهة

والذي يمثل أحد المناهج التي يجب أن نكتب شيفرتها ضمن أصناف المجموعات ()Removeالمنهج

.DictionaryBaseالمخصصة أصناف المجموعات التي ننشئها نحن والتي تعتمد على الصنف

في القسم السابق حيث قمنا باشتقاق الصنف Animalsتبين الشيفرة التالية إصدارا بديال عن الصنف

Animals من الصنف األساسDictionaryBase ولقد ضمنا المنهجينAdd() وRemove()

باإلضافة إلى مفهرس مفتاحي:

public class Animals:DictionaryBase { public void Add1(string newID,Animal newAnimal) { Dictionary.Add(newID, newAnimal); } public void Remove1(string animalID) { Dictionary.Remove(animalID); } public Animals () {

Page 145: مستويات مبتدئ و متوسط -ج2

- 144 - إعداد: المهندس حسام الدين الرز

} public Animal this[string animalID] { get { return (Animal)Dictionary[animalID]; } set { Dictionary[animalID] = value; } }

}

السابق نالحظ ان األول يرث الصنف األساس Animalsالحالي مع الصنف Animalsبمقارنة الصنف

CollectionBase بينما يرث األخير الصنف األساسDictionaryBase وعلى الرغم من ان المناهج

ضاء األول وأع Animalsوالخصائص هي نفسها إال ان هناك بعض االختالفات بين أعضاء الصنف

هذا: Animalsالصنف

إلى المجموعة عنصر إضافةيأخذ منهجAdd1() بارامترين األول يمثل مفتاح العنصر والثاني

العضو DictionaryBaseيمثل العنصر المراد إضافته إلى المجموعة يتضمن الصنف األساس

Dictionary إن هذا العضو هو عضو للواجهةIDictionary ساس المزود بها الصنف األ

DictionaryBase.

أما منهج إزالة عنصر من المجموعةRemove() فهو يأخذ بارامترا واحدا يمثل مفتاح العنصر

بدال من استخدام موقع العنصر )دليله( من المجموعة حيث سيتم إزالة العنصر ذو القيمة المفتاحية

المحددة ضمن بارامتر هذا التابع.

ستخدم أيضا بارامترا نصيا من نوع العضو األخير هو المفهرس والذي يString يمثل مفتاح

castingالعنصر بدال من استخدام دليل العنصر في المجموعة والحظ أن علينا استخدام التشكيل

.Objectيعيد قيمة من نوع Dictionaryهنا ألن المنهج

وعات التي تعتمد والمجم DictionaryBaseهناك فرق آخر بين المجموعات التي تعتمد على الصنف

مع foreachإن استخدام foreachوهو في طريقة استخدام الحلقة CollectionBaseعلى الصنف

DictionaryEntryيعيد لنا كائنات من نوع البنية DictionaryBaseاألصناف المشتقة من الصنف

Animal وللحصول على كائنات System.Collectionsوهو نوع آخر معرف ضمن فضاء األسماء

في هذه البنية keyفي هذه البنية ويمكننا أيضا استخدام العضو Valueنفسها يجب أن نستخدم الخاصية

للحصول على قيمة مفتاح العنصر.

:CollectionBaseبالعودة إلى شيفرة المرور عبر عناصر مجموعة

foreach (Animal myAnimal in animalcollection ) { myAnimal.Feed(); }

Page 146: مستويات مبتدئ و متوسط -ج2

- 145 - إعداد: المهندس حسام الدين الرز

علينا كتابة شيفرة كما DictionaryBaseلكي نتمكن من كتابة شيفرة مشابهة لتلك ولكن مع مجموعات

يلي:

foreach (DictionaryEntry myEntry in animalcollection) { ((Animal)myEntry.Value).Feed();

}

بصورة مباشرة ضمن حلقات Animalمن الممكن في الحقيقة تجاوز هذا السلوك والوصول إلى عناصر

foreach .إال أن هذا الموضوع معقد ولن نتحدث في تفاصيل ذلك في هذا الكتاب

التحديث األول لمكتبة أصناف أوراق اللعب:

Upgrading CardLibrary part1:

ولقد CardLibraryي نهاية الفصل السابق بإنشاء مشروع لمكتبة أصناف ورق اللعب باسم فلقد قمنا

والذي يمثل Deckالذي يمثل ورقة لعب واحدة باإلضافة إلى الصنف Cardاحتوى على الصنف

ذا لقد أنشأنا له Cardيمثل مجموعة من كائنات Deckمجموعة أوراق اللعب كاملة أي أن الصنف

الغرض مصفوفة بسيطة تؤدي المهمة بصورة ال بأس بها.

سوف نقوم في هذا الفصل بإضافة صنف جديد إلى هذه المكتبة وسوف نعيد تسمية مكتبة األصناف باالسم

CardLibrary1 الصنف الجديد هوCards والذي يمثل مجموعة مخصصة لكائناتCard وبواسطة

يزات التي تحدثنا عنها مسبقا في هذا الفصل الشيفرة الكاملة للملف هذه المجموعة فإننا سنحصل على المم

Cards.cs .هي كالتالي

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Collections; namespace CardLibrary1 { public class Cards:CollectionBase { public void Add1(Card newCard) { List.Add(newCard); } public void Remove1(Card oldCard) { List.Remove(oldCard); } public Cards () { }

Page 147: مستويات مبتدئ و متوسط -ج2

- 146 - إعداد: المهندس حسام الدين الرز

public Card this[int cardIndex] { get { return (Card)List[cardIndex]; } set { List[cardIndex] = value; } } public bool Contains1(Card card) { return InnerList.Contains(card); } } }

توضيح:

يأخذ هذا المنهج بارامترا boolيعيد قيمة منطقية من نوع Contains1نالحظ إضافة منهج جديد باسم

تحتوي على هذا الكائن أم ال أي أنه يمكننا ويقوم بالتحقق فيما إذا كانت المجموعة Cardمن النوع

استخدام هذا المنهج للتحقق فيما إذا كانت مجموعة أوراق اللعب تحتوي على ورقة لعب معينة أم ال.

لكي يتمكن من استخدام المجموعة الجديدة تلك بدال من استخدام Deck.csبعد ذلك علينا تعديل الملف

المصفوفة:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CardLibrary1 { public class Deck { private Cards cards = new Cards(); public Deck () { // Cards = new Card[52]; for (int suitVal=0;suitVal <4;suitVal ++) { for (int rankVal=1;rankVal <14;rankVal ++) { cards.Add1(new Card((Suit)suitVal, (Rank)rankVal)); } } } public Card GetCard(int cardNum) { if (cardNum >= 0 && cardNum <= 51)

Page 148: مستويات مبتدئ و متوسط -ج2

- 147 - إعداد: المهندس حسام الدين الرز

return cards[cardNum]; else throw (new System.ArgumentOutOfRangeException("cardNum", cardNum, "Value must be between 0 and 51.")); } public void Shuffle() { //Card[] newDeck = new Card[52]; Cards newDeck = new Cards(); bool[] assigned = new bool[52]; for (int i=0;i<52;i++) { int sourceCard = 0; bool foundCard = false; Random sourceGen = new Random(); while (foundCard ==false ) { sourceCard = sourceGen.Next(52); if (assigned[sourceCard] == false) foundCard = true; } assigned [sourceCard ]=true ; newDeck.Add1(cards[sourceCard]); } cards = newDeck; } } }

مالحظة:

إلى CdrdLibraryالحظ أن علينا تعديل تصريحات فضاءات األسماء عند نسخ ملفات المصدر من

CardLibrary1 وذلك لإلشارة إلى فضاء األسماءCardLibrary1 إن هذا مطبق أيضا على

أيضا. ConsoleCardClientوالتطبيق الزبون Card.csالملف

تستوجب مناقشتها في هذه الشيفرة فمعظم التعديالت التي أجريناها في الشيفرة أعاله ليست هناك تعديالت

الجديدة التي تحمل االسم Cardsبمنطق خلط األوراق عشوائيا وحقيقة إضافة أوراق اللعب إلى مجموعة

newDeck من دليل عشوائي في أوراق اللعب وذلك بدال من إضافتها إلى دليل عشوائي في المصفوفة

newCards من موقع تتابعي في المجموعةcards.

الذي استخدمناه لتجربة مكتبة األصناف ConsoleCardClientيمكننا استخدام برنامج الزبون

CardLibrary مع مكتبة األصناف الجديدة حيث سنحصل على النتائج نفسها وذلك باعتبار أن

يمكن لتطبيق الزبون اآلن االستفادة من صنف لم تتغير ولكن Deckتواقيع)تصريحات( المناهج للصنف

.Cardبدال من االعتماد على مصفوفة لكائنات من النوع Cardsمجموعة

Page 149: مستويات مبتدئ و متوسط -ج2

- 148 - إعداد: المهندس حسام الدين الرز

التحميل الزائد للعوامل:

Operator Overloading:

Operator overloadingالموضوع التالي الذي سنتناوله اآلن يتحدث عن التحميل الزائد للعوامل

مع األصناف التي نقوم <ميل الزائد للعوامل من استخدام العوامل القياسية مثل + وتمكننا تقنية التح

بإنشائها بأنفسنا لقد سمي هذا السلوك بالتحميل الزائد وذلك ألننا نقوم بإعطاء معان خاصة لتلك العوامل

محددة إن هذا بارامتراتغير المعاني العادية التي تحملها هذه العوامل وذلك عند استخدامها مع أنواع

جديدة مختلفة للمناهج. بارامتراتد ما لسلوك وضع تواقيع أنواع حمشابه إلى

إن استخدام التحميل الزائد للعوامل مفيد جدا باعتبار أنه يمكننا القيام بأية معالجة نود القيام بها ضمن

امل األساسي كمثال المخصص لهذا العامل المحمل بصورة زائدة وهو ما قد يتجاوز منطق الع التزويد

العامل+ الذي يعني إضافة الحد األول إلى الحد الثاني سوف نرى قريبا مثاال جيدا لذلك عندما نقوم بتحديث

سوف نقوم بتزويد األصناف بتزويدات خاصة لعوامل CardLibraryآخر لمكتبة أصناف ورق اللعب

ي لعب فيما إذا كانت إحداها تغلب األخرى.المقارنة بحيث نتمكن من استخدام هذه العوامل لمقارنة ورقت

تتم مقارنة أوراق اللعب باالعتماد على اشكال األوراق وهي متمثلة بأربعة مجموعات فقط كما نعلم القلب

واألمر هنا ليس مجرد مقارنة أرقام )رتب( أوراق اللعب والبستوني والسباتي والديناري

فإن ورقة اللعب األولىمختلف عن شكل ورقة اللعب األولى وحسب فإن كان شكل ورقة اللعب الثانية

ستربح بغض النظر عن رتبة ورقة اللعب الثانية إن كيفية تحديد ترتيب الورقة التي تم طرحها قبل الثانية

ي حاالت أللعاب أخرى يمكن ان يكون لدينا شكل رابح دائما فمد على ترتيب الحدود في العامل ويعت

Trump ورقة تنتمي إلى الشكل الرابح هي اللعبة الطرنيب وفي حالة كهذه فإن الورقة التي كالطرنيب في

الرابحة دوما بغض النظر عن شكل الورقة األخرى أو ترتيب طرح الورقتين أثناء اللعب هذا يعني أن

Card1أي أن ورقة اللعب trueفيما إذا كانت مساوية لـ Card1>Card2احتساب نتيجة المقارنة

أوال ال يعني بالضرورة ان نتيجة المقارنة Card1إذا تم طرح ورقة اللعب Card2لب ورقة اللعب ستغ

Card2>Card1 ستأخذ القيمةfalse فإن لم تكن ورقتي اللعبCard1 وCard2 كلتاهما تنتميان إلى

ين السابقتين ارنتالشكل الرابح دوما الطرنيب وإنما إلى شكلين مختلقين ففي هذه الحالة ستكون نتيجة المق

.trueمساوية للقيمة

كبداية لنلق نظرة على الصيغة األساسية للتجميل الزائد للعوامل.

يمكننا تحميل العوامل بصورة زائدة وذلك بإضافة أعضاء أنواع العوامل إلى الصنف وهي يجب أن تكون

الذي يمكن (-)ل العامل دوما قد يكون لبعض العوامل استخدامات متعددة مث Static أعضاء ستاتيكية

استخدامه كعامل أحادي أو ثنائي ولذلك فإن علينا تحديد عدد الحدود التي سيتعامل معها العامل باإلضافة

إلى تحديد أنواع الحدود أيضا وبصورة عامة فإن الحدود التي سنتعامل معها ستكون من نوع واحد دائما

أن بإمكاننا تعريف العوامل لكي تتعامل مع حدود تنتمي وهو الصنف الذي تم تعريف العامل فيه مع العلم

إلى أنواع مختلفة كما سنرى كيفية ذلك الحقا.

Page 150: مستويات مبتدئ و متوسط -ج2

- 149 - إعداد: المهندس حسام الدين الرز

المعرف كما يلي: AddClass1وكمثال لنفترض الصنف البسيط

public class AddClass1 { public int val;

}

آلن إذا استخدمنا شيفرة كما يلي وفقا للصنف السابق وا intيمثل هذا الصنف صنفا مغلفا لقيمة من نوع

فإن المترجم سيعطي رسالة خطأ:

AddClass1 op1=new AddClass1 (); op1 .val =5; AddClass1 op2 = new AddClass1(); op2.val = 7;

op3 = op1 + op2; AddClass1

يمكنك أن تالحظ من رسالة الخطأ الناتجة أن الخطأ حاصل من أن العامل + ال يمكن تطبيقه على حدود

.وذلك باعتبار أنه ليس هناك تعريف لهذه العوامل الستخدامها وفقا لذلك AddClass1من النوع

Error 1 Operator '+' cannot be applied to operands of type

'ConsoleApplication1.AddClass1' and 'ConsoleApplication1.AddClass1'

إن شيفرة كالشيفرة التالية ستعمل بصورة نظامية مع أنها لن تعطينا النتيجة التي نبتغيها:

AddClass1 op1=new AddClass1 (); op1 .val =5; AddClass1 op2 = new AddClass1(); op2.val = 7;

op3 = op1 == op2; bool

مع المتحول op1لقد تم هنا مقارنة المتحول falseالقيمة op3بعد تنفيذ شيفرة كهذه سيأخذ المتحول

op2 وذلك باستخدام العامل المنطقي == وذلك لمعرفة فيما إذا كان كال المتحولين يشيران إلى الكائن

حتى إن كانت falseلتحقق من أن قيمتهما متساوية وهذا يعني أن نتيجة المقارنة ستبقى نفسه أم ال وليس ل

طالما ان المتحولين ال يشيران إلى الكائن نفسه. op2.valمساوية لقيمة op1.valقيمة

بصورة زائدة فإننا سنستخدم شيفرة كما يلي: +ولكي نتمكن من تحميل العامل

public class AddClass1 { public int val; public static AddClass1 operator+(AddClass1 op1,AddClass1 op2) { AddClass1 returnVal = new AddClass1(); returnVal.val = op1.val + op2.val; return returnVal; }

}

Page 151: مستويات مبتدئ و متوسط -ج2

- 150 - إعداد: المهندس حسام الدين الرز

وكما ترى هنا فإن التصريح عن التحميل الزائد للعامل مشابه لشيفرة التصريح عن منهج ستاتيكي فيما

ورمز العامل بدال من اسم المنهج. operatorعدا أننا نستخدم الكلمة المفتاحية

قمية كما لو كنا نستخدمه مع أية قيم ر AddClass1يمكننا اآلن استخدام العامل + مع كائنات من النوع

كمثال:

op3 = op1 + op2; AddClass1

لزائد للعوامل المنطقية يتم بنفس الصورة أما التحميل الزائد للعوامل األحادية فهو يتم بنفس االتحميل

واحدا بدال من اثنين: بارامتراالصورة أيضا فيما عدا أننا نستخدم هنا

public class AddClass1 { public int val; public static AddClass1 operator+(AddClass1 op1,AddClass1 op2) { AddClass1 returnVal = new AddClass1(); returnVal.val = op1.val + op2.val; return returnVal; } public static AddClass1 operator -(AddClass1 op1) { AddClass1 returnVal = new AddClass1(); returnVal.val = -op1.val; return returnVal; }

}

نف سه وهو نوع الصنالحظ اننا عرفنا هنا عاملين بصورة زائدة حيث يطبق كال العاملين على النوع نف

المعرف ضمنه ويعيدان قيمة من نفس نوع الصنف أيضا لنأخذ على سبيل المثال شيفرة األصناف التالية:

public class AddClass1 { public int val; public static AddClass3 operator+(AddClass1 op1,AddClass2 op2) { AddClass3 returnVal = new AddClass3(); returnVal.val = op1.val + op2.val; return returnVal; } } public class AddClass2 { public int val; } public class AddClass3 { public int val;

}

واآلن يمكننا استخدام الشيفرة السابقة كما يلي:

Page 152: مستويات مبتدئ و متوسط -ج2

- 151 - إعداد: المهندس حسام الدين الرز

AddClass1 op1=new AddClass1 (); op1 .val =5; AddClass2 op2 = new AddClass2(); op2.val = 5;

op3 = op1 + op2; AddClass3

عامل + مع حدين ينتميان لنوعين مختلفين وهذا ممكن طالما اننا أضفنا تعريفا نالحظ هنا أننا استخدمنا ال

ويعالج ذلك لكن الحظ أنه لو قمنا بتعريف هذا العامل بنفس AddClass3للعامل + ضمن الصنف

وقمنا بتنفيذ الشيفرة السابقة فإنها لن تعمل وذلك ألننا في حالة لبس AddClass2الصورة ضمن الصنف

ان هناك تعريفان للعامل نفسه وبالتالي علينا ان ننتبه إلى مسألة كهذه وذلك بأن ال نضيف العامل باعتبار

نفسه وبنفس التوقيع إلى أكثر من صنف واحد.

الحظ أيضا عند خلط األنواع كما في العامل السابق علينا ان نراعي مسألة ترتيب ورود الحدود بحيث

يفرة التصريح عن العامل فإذا حاولنا أن نضع الحدود بترتيب مخالففي ش البارامتراتيوافق نفس ترتيب

لترتيب ورودها ضمن توقيع العامل فإن ذلك سيتسبب في حدوث خطأ وبناء على ذلك فإنه ال يمكننا كتابة

شيفرة كما يلي:

op3 = op2 + op1; AddClass3

نعكس فيه ترتيب البارامترات طبعا:إال إذا قمنا بإضافة تحميل زائد آخر للعامل بحيث

public static AddClass3 operator +(AddClass2 op1, AddClass1 op2) { AddClass3 returnVal = new AddClass3(); returnVal.val = op1.val + op2.val; return returnVal;

}

آلن إليك الئحة بالعوامل التي يمكنك تحميلها بشكل زائد:وا

:العوامل األحادية+,-,~,++,--,true, false.

:العوامل الثنائية+,-,*,/,%,&,|,^,<<,>>.

:عوامل المقارنة==,!=,<,>,<=,>=.

مالحظة:

عابير المنطقية بصورة زائدة يمكننا استخدام األصناف ضمن الت falseو trueإذا قمنا بتحميل العاملين

مثال. {}if(op1)ككتابة

إال ان هذا النوع من العوامل ال يمثل =+مثل العامل اإلسنادال يمكننا استخدام التحميل الزائد مع عوامل

عندما وبالتالي فإننا –والعامل =-والعامل +والعامل =+إال حالة خاصة من عوامل ثنائية مثل العامل

سيعمل بصورة موافقة للعامل األساسي وهو العامل+ =+صورة زائدة فإن العامل ب +نقوم بتحميل العامل

يدخل في ذلك أيضا فمن المنطقي جدا أن نحمل هذا العامل بشكل زائد باعتبار =إن عامل اإلسناد الطبيعي

Page 153: مستويات مبتدئ و متوسط -ج2

- 152 - إعداد: المهندس حسام الدين الرز

من ان لهذا العامل استخدام أساسي على كل حال إن هذا العامل مرتبط بعوامل تحويل األنواع المعرفة

قبل المستخدم وهو ما سنتحدث عنه في القسم التالي.

ال أننا نعلم أيضا أن هذين إ ||و &&الحظ انه ال يمكننا استخدام التحميل الزائد مع العاملين المنطقيين

يعني اننا قمنا &على التوالي ولذا فإن التحميل الزائد للعامل |و &العاملين يمثالن حالة خاصة للعاملين

بالمثل. &&لتحميل الزائد للعامل أيضا با

فال يمكننا <و >هناك بعض العوامل التي يجب ان تحمل بصورة زائدة على هيئة ازواج مثل العامالن

ان نستخدم التحميل الزائد مع احد العوامل إال إذا استخدمناه أيضا مع العامل اآلخر أي أنه ال يمكننا تحميل

بشكل زائد أيضا على سبيل المثال: < لعامل بشكل زائد ما لم نحمل ا >العامل

public class AddClass1 { public int val; public static bool operator >=(AddClass2 OP1,AddClass1 OP2) { return (OP1.val >= OP2.val); } public static bool operator <=(AddClass2 OP1, AddClass1 OP2) { return (OP1.val >= OP2.val); } public static bool operator < (AddClass2 op1,AddClass1 op2) { return (op1.val < op2.val); } public static bool operator > (AddClass2 op1, AddClass1 op2) { return (op1.val < op2.val); } } public class AddClass2 { public int val;

}

لكن من األفضل هنا ان نقوم بتجاوز المنهجين =!و ==إن االمر نفسه مطبق على العاملين

Object.Equals() وObject.GetHashCode() باعتبار ان بإمكاننا استخدام هذين المنهجين لمقارنة

الكائنات فبتجاوز هذين المنهجين فإننا نكون واثقين من اننا سنحصل على نتيجة المقارنة نفسها مهما كانت

ستخدامه بهدف الكمالية الطريقة المستخدمة للمقارنة الحظ ان ذلك ليس أمرا واجبا إال أنه من األفضل ا

ال أكثر ولتحقيق ذلك علينا إضافة تزويد جديد للمنهجين كما يلي:

public static bool operator ==(AddClass2 OP1, AddClass1 OP2) { return (OP1.val == OP2.val); } public static bool operator !=(AddClass2 OP1, AddClass1 OP2) {

Page 154: مستويات مبتدئ و متوسط -ج2

- 153 - إعداد: المهندس حسام الدين الرز

return !(OP1.val == OP2.val); } public override bool Equals(object op1) { //return base.Equals(obj); return val == ((AddClass1)op1).val; } public override int GetHashCode() { //return base.GetHashCode(); return val;

}

هذا التوقيع نستخدمونحن يجب أن Objectمن النوع بارامترايستخدم ()Equalsالحظ هنا ان المنهج

تجاوزه وسنتمكن من استخدام التزويد االفتراضي او علينا استخدام التحميل الزائد لهذا المنهج بدال من

للحصول على النتيجة المطلوبة. castالتشكيل استخداممن ضمن هذا الصنف وفي تلك الحالة يجب

فريدة للكائن وذلك باالعتماد على حالته يمكننا intللحصول على قيمة GetHashCodeيستخدم المنهج

أيضا. intا من النوع باعتبار أنه valهنا استخدام القيمة

عوامل التحويل:

Conversion Operators:

لقد رأينا في القسم السابق كيفية التحميل الزائد للعوامل الرياضية وهذا يعني إمكانية استخدام هذه العوامل

#Cمع أنواع معطيات مخالفة للمنطق العام الذي تستخدم ألجله سنتعرف هنا أيضا على ميزة أخرى للغة

ي ن هذا مهم فإع سواء المطلقة منها أو الصريحة ي تمكننا من تعريف عمليات التحويل بين األنواوالت

كثير من المواضع التي نحتاج فيها إلى تحويل أنواع غير مرتبطة مع بعضها البعض فعلى سبيل المثال

بينها. إذا لم تكن هناك أية عالقة وراثية بين هذه األنواع وليست هناك أية واجهات مشتركة

إن convClass2و convClass1بين النوع implicitلنفترض اننا نود تعريف عملية تحويل مطلقة

هذا يعني انه يمكننا كتابة شيفرة كما يلي:

convClass1 op1 = new convClass1(); op2 = op1; convClass2

توضيح:

صناف والواجهات والبنى والتعدادات باإلضافة إلى األنواع تذكر أن ما نقصده باألنواع يشمل األ

بصورة عامة. NET.األساسية التي يوفرها إطار عمل

وبصورة بديلة لنفترض اننا نود تعريف عملية تحويل صريحة بين النوعين السابقين وهذا يعني انه يمكننا

كتابة شيفرة كما يلي:

Page 155: مستويات مبتدئ و متوسط -ج2

- 154 - إعداد: المهندس حسام الدين الرز

convClass1 op1 = new convClass1(); )op1;convClass2op2 = ( convClass2

وكمثال لنأخذ الشيفرة التالية:

public class convClass1 { public int val; public static implicit operator convClass2 (convClass1 op1) { convClass2 retrunVal=new convClass2 (); retrunVal .val =op1 .val ; return retrunVal ; } } public class convClass2 { public double val; public static explicit operator convClass1(convClass2 op1) { convClass1 retrnVal = new convClass1(); checked { retrnVal.val = (int)op1.val; } return retrnVal; }

}

باإلضافة إلى تعريف convClass2و convClass1في البداية تتلخص هذه الشيفرة بتعريف صنفين

والثانية لتحويل قيم convClass2إلى convClass1عالقتي تحويل بين النوعين األولى لتحويل قيم

convClass2 إلىconvClass1.

يتضمن convClass2والنوع intمن نوع يتضمن convClass1نالحظ هنا أن النوع او الصنف قيمة

فهذا يعني doubleضمن مجال قيم النوع محتوى intوبما ان مجال قيم النوع doubleقيمة من النوع

لذا فإن تحويال كهذا intلن يسبب أية مشاكل مهما كانت قيمة doubleإلى intأن تحويل القيم من النوع

بتعريف تحويل مطلق بين النوعين convClass2ولقد قمنا ضمن الصنف implicitهو تحويل مطلق

convClass1 و convClass2 ويل العكسي بين النوعين لكن الحظ ان التحint وdouble ال يمثل

لذا قمنا بتعريف تحويل intيتعدى مجال قيم النوع doubleتحويال مطلقا وهذا ألن مجال قيم النوع

.explicitصريح

في سطر تصريح التحويل وبناء explicitو implicitالحظ أيضا أننا استخدمنا الكلمتين المفتاحيتين

بقة فإن كتابة شيفرة كما يلي هو أمر مقبول تماما:على الشيفرة السا

convClass1 op1 = new convClass1(); op1.val = 3;

op2 = op1; convClass2

Page 156: مستويات مبتدئ و متوسط -ج2

- 155 - إعداد: المهندس حسام الدين الرز

كما castingلكن يتطلب التحويل في االتجاه المعاكس عملية تحويل صريحة تستوجب استخدام التشكيل

يلي:

convClass2 op1 = new convClass2(); op1.val = 3e15;

)op1;convClass1op2 = ( convClass1

وهذا يعني أننا سنحصل على checkedالحظ أننا استخدمنا في تعريف التحويل الصريح الكلمة المفتاحية

أكبر بكثير من أن تتسع ضمن op1للمتحول valاعتراض بعد تنفيذ الشيفرة السابقة ألن قيمة الحقل

.intتقع خارج مجال قيم النوع 3e15وهذا منطقي ألن القيمة op2للمتحول val1الحقل

لمكتبة أصناف أوراق اللعب: التحديث الثاني

Upgrading CardLibrary part2:

نف يزة التحميل الزائد لصواآلن سنقوم بتحديث مشروع مكتبة أصناف ورق اللعب مجددا وذلك بإضافة م

وذلك لكي نسمح بتحديد الشكل الرابح Cardعلينا أن نضيف حقوال جديدة للصنف Cardورقة اللعب

دوما الطرنيب باإلضافة إلى خيار يحدد فيما إذا كان اآلس أكبر من جميع الرتب األخرى سنجعل هذه

كائنات ين فإن هذه القيمة ستسري على جميع الالحقول حقوال ستاتيكية وبالتالي عند وضع قيمة لهذين الحقل

.Cardمن نوع

public class Card { public static bool useTrumps = false; public static Suit trump = Suit.Clup; public static bool isAceHigh = true; public readonly Suit suit; public readonly Rank rank; public override string ToString() { //return base.ToString(); return "The "+rank +" of "+suit+"s"; } private Card () { } public Card (Suit newSuit,Rank newRank) { suit = newSuit; rank = newRank; }

}

وذلك لكي نتمكن من تهيئة مجموعات Deckوبما اننا قمنا بذلك فمن الجدير أن نضع مناهج البناء للصنف

أوراق اللعب بصفات مختلفة.

using System;

Page 157: مستويات مبتدئ و متوسط -ج2

- 156 - إعداد: المهندس حسام الدين الرز

using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CardLibrary1 { public class Deck { private Cards cards = new Cards(); public Deck () { // Cards = new Card[52]; for (int suitVal=0;suitVal <4;suitVal ++) { for (int rankVal=1;rankVal <14;rankVal ++) { cards.Add1(new Card((Suit)suitVal, (Rank)rankVal)); } } } public Deck (bool isAceHigh):this() { Card.isAceHigh = isAceHigh; } public Deck (bool useTrumps,Suit trump):this() { Card.useTrumps = useTrumps; Card.trump = trump; } public Deck (bool isAceHigh,bool useTrumps,Suit trump):this() { Card.isAceHigh = isAceHigh; Card.useTrumps = useTrumps; Card.trump = trump; } public Card GetCard(int cardNum) { if (cardNum >= 0 && cardNum <= 51) return cards [cardNum]; else throw (new System.ArgumentOutOfRangeException("cardNum", cardNum, "Value must be between 0 and 51.")); } public void Shuffle() { //Card[] newDeck = new Card[52]; Cards newDeck = new Cards(); bool[] assigned = new bool[52]; for (int i=0;i<52;i++) { int sourceCard = 0; bool foundCard = false; Random sourceGen = new Random();

Page 158: مستويات مبتدئ و متوسط -ج2

- 157 - إعداد: المهندس حسام الدين الرز

while (foundCard ==false ) { sourceCard = sourceGen.Next(52); if (assigned[sourceCard] == false) foundCard = true; } assigned [sourceCard ]=true ; newDeck.Add1(cards[sourceCard]); } cards = newDeck; } } }

الذي تعرفنا عليه في الفصل الثاني من هذا ()thisلقد عرفنا مناهج البناء الثالثة السابقة باستخدام الكلمة

الجزء وبالتالي ففي جميع األحوال سيتم استدعاء منهج البناء االفتراضي الذي يقوم بتهيئة مجموعة أوراق

اللعب قبل استدعاء المنهج غير االفتراضي.

()Equalsنهجين بعد ذلك سنقوم بإضافة بعض التحميالت الزائدة للعوامل باإلضافة إلى تجاوز الم

:Cardفي الصنف Objectللصنف األب ()GetHashTableو

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CardLibrary1 { public enum Suit { Clup, Diamond, Heart, Spade } public enum Rank { Ace = 1, Deuce, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King }

Page 159: مستويات مبتدئ و متوسط -ج2

- 158 - إعداد: المهندس حسام الدين الرز

public class Card { public static bool useTrumps = false; public static Suit trump = Suit.Clup; public static bool isAceHigh = true; public readonly Suit suit; public readonly Rank rank; public override string ToString() { //return base.ToString(); return "The "+rank +" of "+suit+"s"; } private Card () { } public Card (Suit newSuit,Rank newRank) { suit = newSuit; rank = newRank; } public static bool operator==(Card card1,Card card2) { return (card1.suit == card2.suit) && (card1.rank == card2.rank); } public static bool operator !=(Card card1,Card card2) { return !(card1 == card2); } public override bool Equals(object card) { //return base.Equals(obj); return this == (Card)card; } public override int GetHashCode() { //return base.GetHashCode(); return 12 * (int)rank + (int)suit; } public static bool operator >(Card card1,Card card2) { if (card1 .suit ==card2 .suit ) { if (isAceHigh) return (card1.rank > card2.rank) ^ (card2.rank == Rank.Ace); else return (card1.rank > card2.rank); } else { if (useTrumps && (card2.suit == Card.trump)) return false; else return true; }

Page 160: مستويات مبتدئ و متوسط -ج2

- 159 - إعداد: المهندس حسام الدين الرز

} public static bool operator < (Card card1,Card card2) { return !(card1 >= card2); } public static bool operator >=(Card card1,Card card2) { if (card1 .suit ==card2 .suit ) { if (isAceHigh) return (card1.rank >= card2.rank) ^ (card2.rank == Rank.Ace); else return (card1.rank >= card2.rank); } else { if (useTrumps && (card2.suit == Card.trump)) return false; else return true; } } public static bool operator <=(Card card1,Card card2) { return !(card1 > card2); } } }

قي قد بعض الشيء للعامل المنطعليس هناك ما يحتاج للتوضيح في هذه الشيفرة فيما عدا االستخدام الم

XOR وإذا تتبعنا القيم الممكنة لسطر الشيفرة <و >ا العامل مع التحميل الزائد للعاملين لقد استخدم هذ

الذي يستخدم هذا العامل سنالحظ كيفية عمله وضرورته هنا.

أن تكون الورقة card1حيث يفترض لورقة اللعب card2و card1نحن نقوم بمقارنة ورقتي لعب هنا:

وكما ناقشنا ذلك مسبقا فإن هذا العامل يصبح ضروريا جدا عند المطروحة أوال أي التي تم لعبها أوال

استخدام أوراق اللعب الرابحة دوما وهي أوراق الطرنيب باعتبار أن ورقة الطرنيب تربح دوما أوراق

اللعب من غير فئة الطرنيب حتى إن كانت رتبة ورقة الطرنيب هي األدنى وبالطبع إذا كانت كلتا الورقتين

الشكل نفسها حتى وإن كانت من فئة الطرنيب فإن ورقة اللعب الرابحة هي ورقة اللعب ذات من الفئة أو

األولى تقوم بالتحقق من ذلك: ifالرتبة األكبر لذا فإن التعليمة الشرطية

public static bool operator >(Card card1,Card card2) { if (card1 .suit ==card2 .suit )

{

فهذا يعني أن اآلس هو أعلى رتبة في الفئة وبالتالي trueتساوي isAceHighواآلن إذا كانت قيمة العلم

وقيمته Rankضمن التعداد 1ال يمكننا مجرد مقارنة رتبتي الورقتين مباشرة حيث أن قيمة اآلس هي

^ المنطقي فنحن نقوم هنا بعمليتي مقارنة األولى على أقل من جميع الرتب األخرى هنا يأتي دور العامل

Page 161: مستويات مبتدئ و متوسط -ج2

- 160 - إعداد: المهندس حسام الدين الرز

قيمة الرتبة والثانية لمعرفة فيما إذا كانت الرتبة هي اآلس او ال إن االحتماالت المتوقعة في هذه الحالة

هي:

إذا كانت رتبة الورقةcard1 أكبر من رتبة الورقةcard2 ولم تكن رتبة الورقةcard2 هي

هي األعلى. card1اآلس فإن الورقة

إذا كانت رتبة الورقةcard1 أكبر من رتبة الورقةcard2 ولم تكن رتبة الورقةcard2 هي

هي األعلى. card2اآلس فإن الورقة

إذا كانت رتبة الورقةcard1 أصغر من رتبة الورقةcard2 ولم تكن رتبة الورقةcard1 هي

هي األعلى. card2اآلس فإن الورقة

الورقة إذا كانت رتبةcard1 أصغر من رتبة الورقةcard2 ولم تكن رتبة الورقةcard1 هي

هي األعلى. card1اآلس فإن الورقة

يمكننا تمثيل ذلك بالجدول التالي:

card2.rank=Rank.Ace card1>card2 نتيجة المقارنة

true false true

false true true

false false false

true true false

أو ^: XORاسترجاع كيفية العامل المنطقي وب

op1^op2 op2 op1

true false true

false true true

false false false

true true false

الحظ أن المنطق هنا متطابق تماما وبالتالي فإن استخدام العامل ^ سيعطينا النتيجة التي نبتغيها:

if (isAceHigh) return (card1.rank > card2.rank) ^ (card2.rank == Rank.Ace);

else

مالحظة:

إن استخدام العامل ^ في حاالت كهذه يعد أمرا ذكيا والقليل من المبرمجين الذين يمكنهم التفكير بهذا

على النتيجة نفسها التي يمكن للحصول ifاألسلوب فمعظم المبرمجين يلجئون إلى تعشيش تعابير

الحصول عليها في سطر واحد.

Page 162: مستويات مبتدئ و متوسط -ج2

- 161 - إعداد: المهندس حسام الدين الرز

فإن هذا يعني أن اآلس ال يمثل الرتبة األعلى وهو في موقعه falseتساوي isAceHighإن كانت قيمة

وبالتالي فإن عملية المقارنة البسيطة بين رتبتي ورقتي اللعب كافية: Rankمن التعداد

else return (card1.rank > card2.rank);

}

الجزء المتبقي من هذه الشيفرة متعلق بالحالة التي تكون فيها فئة ورقتي اللعب مختلفة عندئذ سنستخدم

والذي يشير فيما إذا كانت هناك فئة رابحة أي فئة طرنيب ضمن useTrumpالمتحول الستاتيكي

وكانت ورقة اللعب من فئة trueتساوي useTrumpب أم ال فإن كانت قيمة العلم مجموعة أوراق اللع

ليست من فئة الطرنيب فلكي نصل إلى هذه النقطة card1اللعب إن ورقةالطرنيب فإننا نستطيع ان نقول

ألعلى هي الورقة ا card2من الشيفرة يجب ان تكون كلتا الورقتين من فئتين مختلفتين لذا فإن ورقة اللعب

في هذه الحالة:

else { if (useTrumps && (card2.suit == Card.trump))

;false return

من فئة الطرنيب أو إذا لم تستخدم مجموعة أوراق اللعب سياسة card2ولكن إذا لم تكن ورقة اللعب

هي األكبر الرابحة card1فإن ورقة اللعب falseتساوي useTrumpsا كانت قيمة الطرنيب بتاتا أي إذ

باعتبار أنها الورقة المطروحة أوال:

else return true; }

}

وأما باقي العوامل =<ل هناك عامل آخر سيستخدم نفس األسلوب الذي اتبعناه مع هذا العامل وهو العام

فهي عوامل بسيطة جدا لذا لن نتحدث عنها بالتفصيل.

لمشروع الزبون ()Mainال الستخدام هذه العوامل ضع هذه الشيفرة ضمن التابع اتبين الشيفرة التالية مث

.CardLipraryالذي استخدمناه مسبقا مع أمثلة مكتبة األصناف

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using CardLibrary1; namespace ConsoleCardClient { class Program { static void Main(string[] args) { Deck myDeck = new Deck();

Page 163: مستويات مبتدئ و متوسط -ج2

- 162 - إعداد: المهندس حسام الدين الرز

myDeck.Shuffle(); for (int i=0;i<52;i++) { Card tempCard = myDeck.GetCard(i); Console.Write(tempCard.ToString()); if (i != 51) Console.Write(" , "); else Console.WriteLine(); } Card.isAceHigh = true; Console.WriteLine("Aces are high"); Card.useTrumps = true; Card.trump = Suit.Clup; Console.WriteLine("Clubs are trumps."); Card card1, card2, card3, card4, card5; card1 = new Card(Suit.Clup, Rank.Five); card2 = new Card(Suit.Clup, Rank.Five); card3 = new Card(Suit.Clup, Rank.Ace); card4 = new Card(Suit.Heart, Rank.Ten); card5 = new Card(Suit.Diamond, Rank.Ace); Console.WriteLine("{0}=={1}?{2}", card1.ToString(), card2.ToString(), card1 == card2); Console.WriteLine("{0}!={1}?{2}", card1.ToString(), card3.ToString(), card1 != card3); Console.WriteLine("{0}.Equals({1})?{2}", card1.ToString(), card4.ToString(), card1.Equals(card4)); Console.WriteLine("Cards.Equals({0},{1})?{2}", card3.ToString(), card4.ToString(), Card.Equals(card3, card4)); Console.WriteLine("{0}>{1}?{2}", card1.ToString(), card2.ToString(), card1 > card2); Console.WriteLine("{0}<={1}?{2}", card1.ToString(), card2.ToString(), card1 <= card2); Console.WriteLine("{0}>{1}?{2}", card4.ToString(), card1.ToString(), card4 > card1); Console.WriteLine("{0}>{1}?{2}", card1.ToString(), card4.ToString(), card1 > card4); Console.WriteLine("{0}>{1}?{2}", card5.ToString(), card4.ToString(), card5 > card4); Console.WriteLine("{0}>{1}?{2}", card4.ToString(), card5.ToString(), card4 > card5); } } }

ا في الشكل التالي:والنتيجة ستظهر كم

Page 164: مستويات مبتدئ و متوسط -ج2

- 163 - إعداد: المهندس حسام الدين الرز

(4-5الشكل )

التحويالت المتقدمة:

Advanced Conversions:

#Cلقد تعلمنا حتى اآلن كيفية تعريف عوامل التحويل وبالتالي أصبح باإلمكان مناقشة بعض مزايا لغة

المتعلقة بتحويل األنواع سوف نتناول المواضيع التالية:

حويل بين أنواع القيمة وأنواع المرجع.التغليف وعدم التغليف ويقصد بهما الت

العاملis .والذي يستخدم لتفحص المتحول فيما إذا كان من نوع محدد أو أنه متوافق مع هذا النوع

العاملas والذي يستخدم لتحويل المتحول إلى نوع محدد كأسلوب مختلف عن استخدام التشكيل

casting.

يف وعدم التغليف:التغل

Boxing and Unboxing:

لقد تحدثنا في الفصل األول من هذا الجزء حول االختالف بين أنواع القيمة وأنواع المرجع ولقد عدنا

التي تمثل أنواع قيمة مع Structللحديث عن هذا الموضوع في الفصل الثاني أيضا وذلك بمقارنة بنى

ويل نوع القيمة إلى نوع على أنه عملية تح boxingاألصناف والتي تمثل أنواع مرجع يعرف التغليف

Page 165: مستويات مبتدئ و متوسط -ج2

- 164 - إعداد: المهندس حسام الدين الرز

فيمثل unboxingأو إلى نوع واجهة مزودة عبر نوع القيمة أما عدم التغليف System.Objectالمرجع

العملية المعاكسة للتغليف.

التالية: Structعلى سبيل المثال لنفترض أن لدينا البنية

struct myStruct { public int val;

}

كما يلي: objectتغليف المتحول من هذا النوع إلى متحول من النوع يمكننا

myStruct valType1 = new myStruct(); valType1.val = 5;

refType = valType1; object

ضمن هذه valوأسندنا إلى العضو myStructمن النوع valType1لقد قمنا هنا بإنشاء متحول جديد

يتضمن refTypeباسم objectالبنية قيمة ما ومن ثم قمنا بتغليف هذا المتحول ضمن متحول من النوع

الكائن الذي تم إنشائه بتغليف متحول بهذه الصورة على مرجع لنسخة من متحول نوع القيمة يتضمن

وليس مرجعا لمتحول نوع القيمة األصلي ويمكننا التحقق من ذلك بتعديل محتوى الكائن الذي تم إنشائه

إلى متحول جديد وتفحص محتويات objectالبنية األصلي ومن ثم إزالة تغليف البنية في المتحول من نوع

متحول القيمة الجديد:

myStruct valType1 = new myStruct(); valType1.val = 5; object refType = valType1; valType1.val = 6; myStruct valType2 = (myStruct)refType;

, valType2.val);"valType2.val={0}".WriteLine(Console

ستطبع هذه الشيفرة السطر التالي:

valType2.val=5

لكن عند إسناد نوع مرجعي إلى كائن فإننا نحصل على نتيجة مختلفة يمكننا تمثيل ذلك بتعديل قيمة

myStruct صنف بتجاهل حقيقة أن اسم هذا الصنف لم يعد مناسبا بعد اآلن:الضمن

class myStruct { public int val;

}

لسابقة وتنفيذها سنالحظ الخرج التالي:واآلن وبدون أي تعديل آخر على الشيفرة ا

valType2.val=6

يمكننا تغليف أنواع القيمة ضمن واجهة وذلك طالما أنها مزودة بهذه الواجهة على سبيل المثال لنفترض

كما يلي: IMyInterfaceمزودة بالواجهة myStructأن البنية

Page 166: مستويات مبتدئ و متوسط -ج2

- 165 - إعداد: المهندس حسام الدين الرز

interface IMyInterface { } struct myStruct:IMyInterface { public int val;

}

كما يلي: IMyInterfaceيمكننا عندئذ تغليف البنية ضمن النوع

myStruct valType1 = new myStruct(); refType = valType1; IMyInterface

لعادية:ويمكننا إزالة التغليف أيضا باستخدام صيغة التشكيل ا

)refType;myStructvalType2 = ( myStruct

وكما ترى من هذه األمثلة إن عملية التغليف ال تتطلب شيفرات خاصة لتطبيقها أما عملية إزالة التغليف

لقيمة فهي تتطلب تحويال صريحا لها والذي يتطلب استخدام التشكيل.

مالحظة:

ويل مطلقة أما إزالة التغليف فهي عملية تحويل صريحة.يمكننا أن نعتبر التغليف عملية تح

يمكن ان تتساءل عن سبب استخدامنا للتغليف أساسا في الحقيقة هناك سببان يجعالن استخدام التغليف

أمرا مهما ومفيدا جدا األول أنه يسمح لنا باستخدام أنواع القيمة ضمن المجموعات مثل النوع

ArrayList ال العناصر من النوع حيث ال يتم تخزين إobject أو األنواع المشتقة منه أي جميع الكائنات

والسبب الثاني متمثل في اآللية التي تسمح لنا باستدعاء مناهج الكائن على أنواع القيمة وكمالحظ أخيرة

من الجدير التنويه إلى أن إزالة تغليف القيمة أمر ضروري قبل الوصول إلى محتوياتها.

:isالعامل

The is Operator:

ى إل بارامتربتفحص فيما إذا كان متحول مجهول ربما متحول ممرر ككائن على شكل isيسمح العامل

فهذا يعني أن عملية التحويل trueمنهج ما يمكن تحويله إلى نوع محدد فإذا كانت نتيجة هذا العامل هي

المناهج على الكائنات وذلك لتفحص ما إذا إلى هذا النوع ممكنة يمكننا استخدام هذا العامل قبل استدعاء

كان الكائن من نوع يدعمه المنهج.

توضيح:

ما إذا كان النوعان المتشابهان أم ال وإنما يتفحص فيما إذا كان النوعان متوافقين isال يتفحص العامل

مع بعضهما البعض.

Page 167: مستويات مبتدئ و متوسط -ج2

- 166 - إعداد: المهندس حسام الدين الرز

الصيغة التالية: isيأخذ العامل

<operand> is <type>

توقعة لهذا العامل هي كالتالي:والنتيجة الم

إذا كان<type> يمثل نوع صنف فإن النتيجة هيtrue إذا كان<operand> من هذا النوع

.<type>أو أنه يرث من هذا النوع أو إن كان باإلمكان تغليف نوعه ضمن النوع

إذا كان النوع<type> يمثل نوع واجهة فإن النتيجة هيtrue إذا كان<operand> ن هذا م

النوع أو إذا كان نوعه مزودا بهذه الواجهة.

إذا كان النوع<type> يمثل نوع قيمة فإن النتيجة هيtrue إذا كان<operand> من هذا

.<type>النوع أو إذا كان من النوع الذي يمكننا إزالة تغليفه إلى نوع القيمة

العامل بشكل عملي: سوف نتناول ذلك في المثال التالي لنرى كيفية استخدام هذا

:isتطبيق حول استخدام العامل

.ConsoleISجديد باسم Consoleقم بإنشاء تطبيق -1

كما يلي: Program.csعدل شيفرة الملف -2

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleIS { class Checker { public void Check(object param1) { if (param1 is ClassA) Console.WriteLine("variable can be converted to" + " ClassA."); else Console.WriteLine("variable can't be converted" + " ClassA."); if (param1 is IMyInterface) Console.WriteLine("variable can be converted to" + " IMyInterface."); else Console.WriteLine("variable can't be converted to" + " IMyInterface."); if (param1 is myStruct) Console.WriteLine("variable can be converted to" + " myStruct."); else Console.WriteLine("variable can't be converted to" +

Page 168: مستويات مبتدئ و متوسط -ج2

- 167 - إعداد: المهندس حسام الدين الرز

" myStruct."); } } interface IMyInterface { } class ClassA:IMyInterface { } class ClassB:IMyInterface { } class ClassC { } class ClassD:ClassA { } struct myStruct:IMyInterface { } class Program { static void Main(string[] args) { Checker check = new Checker(); ClassA try1 = new ClassA(); ClassB try2 = new ClassB(); ClassC try3 = new ClassC(); ClassD try4 = new ClassD(); myStruct try5 = new myStruct(); object try6 = try5; Console.WriteLine("analyzing ClassA type variable:"); check.Check(try1); Console.WriteLine("\nanalyzing ClassB type variable:"); check.Check(try2); Console.WriteLine("\nanalyzing ClassC type variable:"); check.Check(try3); Console.WriteLine("\nanalyzing ClassD type variable:"); check.Check(try4); Console.WriteLine("\nanalyzing myStruct type variable:"); check.Check(try5); Console.WriteLine("\nanalyzing boxed myStruct type variable:"); check.Check(try6); Console.ReadLine(); } } }

.F5نفذ الشيفرة بالضغط على زر -3

Page 169: مستويات مبتدئ و متوسط -ج2

- 168 - إعداد: المهندس حسام الدين الرز

(4-6الشكل )

كيفية العمل:

How to Work:

لقد تم تعريف ثالثة أصناف وواجهة isيستعرض هذا المثال النتائج العديدة الممكنة عند استخدام العامل

لرؤية ما إذا كنا نستطيع تحويلها إلى isفي منهج يستخدم العامل كبارامتراتتخدامها وبنية وقد تم اس

أو الواجهة أو البنية. ClassAالنوع

هما النوعان الوحيدان مع النوع ClassAاللذين يرثان من الصنف ClassDو ClassAإن النوعين

ClassA هذا الصنف. فاألنواع التي ال ترث من هذا الصنف ليست متوافقة مع

لذا فإن جميعها IMyInterfaceفجميعها مزود بالواجهة myStructو ClassBو ClassAأما األنواع

وبالتالي فهو متوافق مع ClassAيرث من النوع ClassDالنوع IMyInterfaceمتوافق مع النوع

.IMyInterfaceفهو غير متوافق مع الواجهة ClassCهذه الواجهة أيضا أما النوع

وجميع المتحوالت المغلفة من هذا النوع متوافقة مع البنية myStructوأخيرا فإن المتحوالت من النوع

MyStruct وذلك باعتبار أنه ال يمكننا تحويل أنواع المرجع إلى أنواع قيمة إال أنواع المرجع الناتجة

من تغليف أنواع القيمة.

Page 170: مستويات مبتدئ و متوسط -ج2

- 169 - إعداد: المهندس حسام الدين الرز

:asالعامل

The AS Operator:

بتحويل النوع إلى نوع مرجع محدد وذلك باستخدام الصيغة التالية: asيقوم العامل

<operand> as <type>

وإليك الحاالت الممكنة لهذا العامل:

إذا كان<operand> من النوع<type>.

تحويل أمكنإذا<operand> بشكل مطلق على النوع<type>.

تغليف أمكنإذا<operand> إلى النوع<type>.

على سبيل nullفإن النتيجة هي <type>و <operand>مكن استخدام التحويل الصريح بين ن أإأما

الذي ClassDوالنوع ClassAالمثال بالعودة إلى المثال األخير هناك تحويل صريح ممكن بين النوع

نه وفقا للشيفرة التالية:إوبالتالي ف ClassAيرث من الصنف

ClassA obj1 = new ClassA(); ClassD obj2 = obj1 as ClassD;

.nullالقيمة obj2سيأخذ المتحول

فإن العامل سيعمل وفقا للشيفرة: ClassDكائنا من النوع ClassAأما إن تضمن متحول من النوع

ClassD obj1 = new ClassD(); ClassA obj2 = obj1;

;ClassD asobj3 = obj2 lassDC

إن هذا nullوليس القيمة obj1مرجعا على الكائن نفسه الذي يشير إليه obj3فهنا سيمثل المتحول

مفيدا جدا باعتبار أنه سينتج عن الشيفرة التالية رمي العتراض: asيجعل استخدام العامل

ClassA obj1 = new ClassA(); )obj1;ClassDobj2 = ( assDCl

ن هذا يعني أن شيفرة إ obj2للمتحول nullستعيد القيمة asبينما الشيفرة الموافقة التي تستخدم العامل

اأنشأناها مسبقا في هذا الفصل وهموكمثال لنعد إلى الصنفين اللذين #Cكالسابقة شائعة جدا في تطبيقات

:Animalالصنف الذي يرث من Cowوالصنف Animalالصنف

public void MilkCow(Animal myAnimal) { Cow myCow = myAnimal as Cow; if (myCow !=null) { myCow.Milk(); } else

Page 171: مستويات مبتدئ و متوسط -ج2

- 170 - إعداد: المهندس حسام الدين الرز

{ Console.WriteLine("{0} is n't a cow,and so can't be milked", myAnimal.Name()); }

}

إن هذا أبسط بكثير من التعامل مع االعتراضات الناتجة عن عدم تحقق عملية التحويل الصريحة بين

.Cowوالنوع Animalالنوع

النسخ العميق:

Deep Copying:

باستخدام shallow copyingد رأينا في الفصل الثاني من هذا الجزء كيفية القيام بالنسخ السطحي لق

وباستخدام منهج كالتالي: ()System.Object.MemberwiseCloneالمنهج المحمي

public class cloner { public int val; public cloner (int newVal) { val = newVal; } public object GetCopy() { return MemberwiseClone(); }

}

لنفترض ان لدينا حقوال من أنواع مرجعية بدال من أنواع قيمة )كالكائنات مثال(:

public class Content { public int val; } public class Cloner { public Content MyContent = new Content(); public Cloner (int newVal) { MyContent.val = newVal; } public object GetCopy() { return MemberwiseClone(); }

}

حقال يشير إلى نفس الكائن الذي يشير له الكائن األصلي ()GetCopyوفي حالة كهذه سيعيد المنهج

والشيفرة التالية تستعرض ذلك باستخدام هذا الصنف:

Page 172: مستويات مبتدئ و متوسط -ج2

- 171 - إعداد: المهندس حسام الدين الرز

Cloner mySource = new Cloner(5); Cloner myTarget = (Cloner)mySource.GetCopy(); Console.WriteLine("myTarget.myContent.val={0}", myTarget.MyContent.val); mySource.MyContent.val = 2; Console.WriteLine("myTarget.myContent.val={0}",

myTarget.MyContent.val);

وذلك باستخدام منهج البناء غير Clonerمن النوع mySourceمنا هنا بإنشاء متحول جديد لقد ق

وفي السطر الثاني 5لهذا المتحول وهي MyContent.valاالفتراضي لقد قمنا هنا بوضع قيمة للحقل

على المتحول ()GetCopyباستخدام المنهج Clonerمن نوع myTargetقمنا بإنشاء متحول آخر

mySource والذي يقوم بإعادة نسخة سطحية للكائنmySource ويقوم السطر الثالث بطباعة قيمة

لنسخة المتحول الجديدة. MyContent.valالحقل

مالحظة:

فإن المنهج Clonerالحظ أننا استخدمنا التشكيل في السطر الثاني فكما تالحظ ضمن شيفرة الصنف

GetCopy() يعيد قيمة من النوعObject.

وسينتج عن ذلك mySource.MyContent.valأما في السطر الرابع فقد قمنا بإسناد قيمة جديدة للحقل

وذلك ألن كال المتحولين يشيران إلى الكائن myTargetتغير لقيمة الحقل نفسه في المتحول

MyContent :نفسه والخرج سيصبح بالصورة التالية

(4-7الشكل )

نسخة منفصلة تماما عن النسخة األصلية علينا أن نلجأ الستخدام النسخ العميق إذن كي نتمكن من إنشاء

المستخدم في الشيفرة السابقة ولكن من المفضل ()GetCopyوللقيام بذلك لن نحتاج سوى لتعديل المنهج

القياسية للقيام بذلك وتتلخص هذه الطريقة بالتزود بالواجهة NET.استخدام طريقة إطار عمل

ICloneable والتي تحتوي على منهج واحد فقط هوClone() ال بأخذ هذا المنهج أية بارامترات ويعيد

.()GetCopyتماما كالمنهج objectقيمة من نوع

واآلن لكي نتمكن من استخدام النسخ العميق مع الشيفرة السابقة سنعدلها كما يلي:

public class Content { public int val; } public class Cloner:ICloneable {

Page 173: مستويات مبتدئ و متوسط -ج2

- 172 - إعداد: المهندس حسام الدين الرز

public Content MyContent = new Content(); public Cloner (int newVal) { MyContent.val = newVal; } public object Clone() { Cloner clonedCloner = new Cloner(MyContent.val); return clonedCloner; }

}

المتضمنة في كائن MyContent.valجديد باستخدام قيمة الحقل Clonerلقد قمنا هنا بإنشاء كائن

Cloner من ذلك.األصلي إن هذا الحقل حقل قيمة وليس كائن وبالتالي ال حاجة الستخدام نسخ أعمق

بالمنهج ()GetCopyواآلن إذا استخدمنا شيفرة مشابهة للشيفرة السابقة ولكن باستبدال استدعاء المنهج

Clone() :سيعطينا ذلك النتيجة التالية

(4-8الشكل )

ن نالحظ ان نسخة الكائن الجديدة مستقلة تماما عن الكائن األصلي.واآل

خر وذلك تبعا ألنواع المرجع التي يحتويها كل صنف صنف آلفرة النسخ العميق تختلف من الحظ أن شي

خصوصا ضمن أنظمة ()Cloneوفي حالة كهذه نحن بحاجة إلى استدعاء تعاودي )عميق( للمنهج

نسخا عميقا Clonerللصنف MyContentالحقل الكائنات األكثر تعقيدا على سبيل المثال إذا تطلب

أيضا فإن علينا القيام بما يلي:

public class Content { public int val; } public class Cloner:ICloneable { public Content MyContent = new Content(); public Cloner () { } public Cloner (int newVal) { MyContent.val = newVal; } public object Clone() {

Page 174: مستويات مبتدئ و متوسط -ج2

- 173 - إعداد: المهندس حسام الدين الرز

Cloner clonedCloner = new Cloner(); clonedCloner.MyContent = MyContent; return clonedCloner; }

}

:لمكتبة أصناف أوراق اللعب التحديث الثالث

Upgrading CardLibrary part3:

نات النسخ العميق لكائ إلتاحةطار التنفيذ العملي وذلك اآلن بوضع ما تعلمناه حول النسخ في إسنقوم

ويمكن ان يكون ذلك مفيدا ICloneableوذلك باستخدام الواجهة Deckو Cardsو Cardاألصناف

في بعض ألعاب الورق.

ي حقول هعتبار ان جميع الحقول ضمن الصنف سهل جدا با Cardصنف إن تطبيق ميزة النسخ العميق لل

قيمة وبالتالي يمكننا استخدام النسخ السطحي فقط سنعدل شيفرة الصنف كما يلي:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CardLibrary1 { public enum Suit { Clup, Diamond, Heart, Spade } public enum Rank { Ace = 1, Deuce, Three, Four, Five, Six, Seven, Eight, Nine, Ten, Jack, Queen, King } public class Card:ICloneable {

Page 175: مستويات مبتدئ و متوسط -ج2

- 174 - إعداد: المهندس حسام الدين الرز

public object Clone() { return MemberwiseClone(); } public static bool useTrumps = false; public static Suit trump = Suit.Clup; public static bool isAceHigh = true; public readonly Suit suit; public readonly Rank rank; public override string ToString() { //return base.ToString(); return "The "+rank +" of "+suit+"s"; } private Card () { } public Card (Suit newSuit,Rank newRank) { suit = newSuit; rank = newRank; } public static bool operator==(Card card1,Card card2) { return (card1.suit == card2.suit) && (card1.rank == card2.rank); } public static bool operator !=(Card card1,Card card2) { return !(card1 == card2); } public override bool Equals(object card) { //return base.Equals(obj); return this == (Card)card; } public override int GetHashCode() { //return base.GetHashCode(); return 12 * (int)rank + (int)suit; } public static bool operator >(Card card1,Card card2) { if (card1 .suit ==card2 .suit ) { if (isAceHigh) return (card1.rank > card2.rank) ^ (card2.rank == Rank.Ace); else return (card1.rank > card2.rank); } else { if (useTrumps && (card2.suit == Card.trump)) return false; else

Page 176: مستويات مبتدئ و متوسط -ج2

- 175 - إعداد: المهندس حسام الدين الرز

return true; } } public static bool operator < (Card card1,Card card2) { return !(card1 >= card2); } public static bool operator >=(Card card1,Card card2) { if (card1 .suit ==card2 .suit ) { if (isAceHigh) return (card1.rank >= card2.rank) ^ (card2.rank == Rank.Ace); else return (card1.rank >= card2.rank); } else { if (useTrumps && (card2.suit == Card.trump)) return false; else return true; } } public static bool operator <=(Card card1,Card card2) { return !(card1 >card2 ); } } }

هنا يمثل نسخا سطحيا فليست هناك أية قواعد لتحديد ما يجب ICloneableالحظ أن التزود بالواجهة

وهذا كاف هنا. ()Cloneأن يحدث ضمن المنهج

ن النسخ العميق هنا معقد إ .أيضا ICloneableبالواجهة Cardsبعد ذلك سنقوم بتزويد صنف المجموعة

في المجموعة األصلية إلى المجموعة الناتجة عن Cardبعض الشيء باعتبار أن علينا نسخ كل كائن

النسخ وبالتالي فإننا سنستخدم النسخ العميق بالصورة التالية:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Collections; namespace CardLibrary1 { public class Cards:CollectionBase,ICloneable { public object Clone() { Cards newCards = new Cards();

Page 177: مستويات مبتدئ و متوسط -ج2

- 176 - إعداد: المهندس حسام الدين الرز

foreach (Card sourceCard in List ) { newCards.Add1((Card)sourceCard.Clone()); } return newCards; } public void Add1(Card newCard) { List.Add(newCard); } public void Remove1(Card oldCard) { List.Remove(oldCard); } public Cards () { } public Card this[int cardIndex] { get { return (Card)List[cardIndex]; } set { List[cardIndex] = value; } } public bool Contains1(Card card) { return InnerList.Contains(card); } } }

أيضا هناك مشكلة صغيرة هنا فالصنف ICloneableبالواجهة Deckوأخيرا علينا أن نزود الصنف

Deck ب عدا أنه يمكنه إعادة ترتيب أوراق اللع فيماال يملك أية وسيلة لتعديل أوراق اللعب التي يحتويها

لكي تأخذ أوراقها ترتيبا معينا مثال Deckفقط فليست هناك طريقة لتعديل حالة مجموعة أوراق اللعب

Cardsيسمح بتمرير مجموعة Deckمنهج بناء خاص للصنف لتجاوز هذه المشكلة سنقوم بتعريف

هي كما يلي: Deckعند إنشائه وبالتالي الشيفرة التي سيتم إضافتها إلى الصنف Deckمحددة إلى كائن

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CardLibrary1 { public class Deck:ICloneable { public object Clone()

Page 178: مستويات مبتدئ و متوسط -ج2

- 177 - إعداد: المهندس حسام الدين الرز

{ Deck newDeck = new Deck((Cards)cards.Clone()); return newDeck; } private Deck (Cards newCards) { cards = newCards; } private Cards cards = new Cards(); public Deck () { // Cards = new Card[52]; for (int suitVal=0;suitVal <4;suitVal ++) { for (int rankVal=1;rankVal <14;rankVal ++) { cards.Add1(new Card((Suit)suitVal, (Rank)rankVal)); } } } public Deck (bool isAceHigh):this() { Card.isAceHigh = isAceHigh; } public Deck (bool useTrumps,Suit trump):this() { Card.useTrumps = useTrumps; Card.trump = trump; } public Deck (bool isAceHigh,bool useTrumps,Suit trump):this() { Card.isAceHigh = isAceHigh; Card.useTrumps = useTrumps; Card.trump = trump; } public Card GetCard(int cardNum) { if (cardNum >= 0 && cardNum <= 51) return cards [cardNum]; else throw (new System.ArgumentOutOfRangeException("cardNum", cardNum, "Value must be between 0 and 51.")); } public void Shuffle() { //Card[] newDeck = new Card[52]; Cards newDeck = new Cards(); bool[] assigned = new bool[52]; for (int i=0;i<52;i++) { int sourceCard = 0; bool foundCard = false; Random sourceGen = new Random(); while (foundCard ==false )

Page 179: مستويات مبتدئ و متوسط -ج2

- 178 - إعداد: المهندس حسام الدين الرز

{ sourceCard = sourceGen.Next(52); if (assigned[sourceCard] == false) foundCard = true; } assigned [sourceCard ]=true ; newDeck.Add1(cards[sourceCard]); } cards = newDeck; } } }

ومجددا يمكننا تجربة عملية النسخ باستخدام تطبيق زبون بسيط كما في السابق حيث سنضع الشيفرة التالية

لمشروع تطبيق الزبون: ()Mainضمن المنهج

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using CardLibrary1; namespace ConsoleCardClient { class Program { static void Main(string[] args) { Deck deck1 = new Deck(); Deck deck2 = (Deck)deck1.Clone(); Console.WriteLine("The first card in the original deck is:{0}", deck1.GetCard(0)); Console.WriteLine("The first card in the cloned deck is:{0}", deck2.GetCard(0)); deck1.Shuffle(); Console.WriteLine("Original deck shuffle>"); Console.WriteLine("The first card in the original deck is:{0}", deck1.GetCard(0)); Console.WriteLine("The first card in the cloned deck is:{0}", deck2.GetCard(0)); Deck myDeck = new Deck(); myDeck.Shuffle(); for (int i=0;i<52;i++) { Card tempCard = myDeck.GetCard(i); Console.Write(tempCard.ToString()); if (i != 51) Console.Write(" , "); else Console.WriteLine(); } Card.isAceHigh = true; Console.WriteLine("Aces are high");

Page 180: مستويات مبتدئ و متوسط -ج2

- 179 - إعداد: المهندس حسام الدين الرز

Card.useTrumps = true; Card.trump = Suit.Clup; Console.WriteLine("Clubs are trumps."); Card card1, card2, card3, card4, card5; card1 = new Card(Suit.Clup, Rank.Five); card2 = new Card(Suit.Clup, Rank.Five); card3 = new Card(Suit.Clup, Rank.Ace); card4 = new Card(Suit.Heart, Rank.Ten); card5 = new Card(Suit.Diamond, Rank.Ace); Console.WriteLine("{0}=={1}?{2}", card1.ToString(), card2.ToString(), card1 == card2); Console.WriteLine("{0}!={1}?{2}", card1.ToString(), card3.ToString(), card1 != card3); Console.WriteLine("{0}.Equals({1})?{2}", card1.ToString(), card4.ToString(), card1.Equals(card4)); Console.WriteLine("Cards.Equals({0},{1})?{2}", card3.ToString(), card4.ToString(), Card.Equals(card3, card4)); Console.WriteLine("{0}>{1}?{2}", card1.ToString(), card2.ToString(), card1 > card2); Console.WriteLine("{0}<={1}?{2}", card1.ToString(), card2.ToString(), card1 <= card2); Console.WriteLine("{0}>{1}?{2}", card4.ToString(), card1.ToString(), card4 > card1); Console.WriteLine("{0}>{1}?{2}", card1.ToString(), card4.ToString(), card1 > card4); Console.WriteLine("{0}>{1}?{2}", card5.ToString(), card4.ToString(), card5 > card4); Console.WriteLine("{0}>{1}?{2}", card4.ToString(), card5.ToString(), card4 > card5); } } }

والخرج سيصبح كما يلي:

(4-9الشكل )

Page 181: مستويات مبتدئ و متوسط -ج2

- 180 - إعداد: المهندس حسام الدين الرز

االعتراضات المخصصة:

Custom Exceptions:

لمنا كيفية علقد رأينا في الفصل السابع من الجزء األول من هذا الكتاب كيفية التعامل مع االعتراضات وت

وتتضمن NET.ولقد رأينا في هذا الكتاب عددا من اعتراضات try..catch..finallyاستخدام الكتل

الحظ أنه من المفيد أحيانا إنشاء أصناف System.Exceptionذلك الصنف األساس لالعتراضات

ات ام االعتراضاعتراضاتك المخصصة من هذا الصنف األساس واستخدامها ضمن تطبيقك بدال من استخد

ذا يسمح بتخصيص أكبر للمعلومات الناتجة عن االعتراضات عند اصطيادها فعلى سبيل هالقياسية إن

المثال يمكننا إضافة خاصية لصنف االعتراض بحيث يحجب الوصول إلى بعض المعلومات التحتية

رد عرض معلومات لالعتراضات مما قد يتيح لالعتراض مستقبال القيام بالتعديالت الالزمة أو مج

االعتراض الحاصل.

Visual Studioومتى عرفنا صنف االعتراض يمكننا إضافته إلى الئحة األصناف المصنفة من قبل

يمكنك من خالل صندوق الحوار هذا التحكم في Debugمن القائمة Exceptionباستخدام األمر 2013

غير المعالجة يسمح صندوق الحوار هذا عند رمي االعتراضات Visual Studio 2013كيفية استجابة

من التوقف عن تنفيذ التطبيق وفتح نافذة التنقيح عند حصول االعتراض Visual Studio 2013بتمكين

أو االستمرار بالتنفيذ بداية عند رمي اعتراض من هذا النوع ومن ثم إذا لم يكن االعتراض معالجا.

(4-10الشكل )

Page 182: مستويات مبتدئ و متوسط -ج2

- 181 - إعداد: المهندس حسام الدين الرز

ناف أوراق اللعب:لمكتبة أص التحديث الرابع

Upgrading CardLibrary part4:

يمكننا االستفادة من االعتراضات المخصصة ضمن مكتبة أصناف أوراق اللعب لذا سنقوم بتحديث

قياسي NET.يقوم برمي اعتراض ()Deck.GetCardنالحظ أن المنهج CardLibraryالمشروع

وسنقوم بتعديل ذلك الستخدام اعتراض مخصص.

:Exceptions.csن بحاجة إلى تعريف االعتراض لذا سنعرف صنفا جديدا ضمن الملف أوال نح

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace CardLibrary1 { public class CardOutOfRangeException:Exception { private Cards deckContents; public Cards DeckContents { get { return deckContents; } } public CardOutOfRangeException(Cards sourceDeckContents): base ("There are only 52 cards in the deck.") { deckContents = sourceDeckContents; } } class Exceptions { } }

Cardsن ذلك يسمح بالوصول إلى كائن إ Cardsيجب ان يتم إنشاء حالة من هذا الصنف ضمن الصنف

Exceptionومن ثم توفير رسالة الخطأ المناسبة لمنهج بناء الصنف DeckCotentsاصية من خالل الخ

للصنف. Messageاألساس وبالتالي يصبح متوفرا من خالل الخاصية

أي استبدال Deck.csبعد ذلك سنقوم بإضافة الشيفرة التي تقوم برمي هذا االعتراض في الملف

االعتراض التقليدي السابق.

public Card GetCard(int cardNum) {

Page 183: مستويات مبتدئ و متوسط -ج2

- 182 - إعداد: المهندس حسام الدين الرز

if (cardNum >= 0 && cardNum <= 51) return cards[cardNum]; else throw new CardOutOfRangeException((Cards)cards.Clone());

}

وذلك بهيئة Deckعند النسخ العميق للمحتويات الحالية لكائن DeckContentsسيتم تهيئة الخاصية

إن هذا يعني أنه يمكننا ان نرى المحتويات في النقطة التي حصل عندها الخطأ لذا Cardsكائن من نوع

فإن التعديالت الالحقة بمحتوى مجموعة أوراق اللعب لن تضيع هذه المعلومات.

تطبيق الزبون التالية: لتفحص ذلك يمكننا استخدام شيفرة

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using CardLibrary1; namespace ConsoleCardClient { class Program { static void Main(string[] args) { Deck deck1 = new Deck(); try { Card myCard = deck1.GetCard(60); } catch (CardOutOfRangeException e) { Console.WriteLine(e.Message); Console.WriteLine(e.DeckContents[0]); } Deck deck2 = (Deck)deck1.Clone(); Console.WriteLine("The first card in the original deck is:{0}", deck1.GetCard(0)); Console.WriteLine("The first card in the cloned deck is:{0}", deck2.GetCard(0)); deck1.Shuffle(); Console.WriteLine("Original deck shuffle>"); Console.WriteLine("The first card in the original deck is:{0}", deck1.GetCard(0)); Console.WriteLine("The first card in the cloned deck is:{0}", deck2.GetCard(0)); Deck myDeck = new Deck(); myDeck.Shuffle(); for (int i=0;i<52;i++) { Card tempCard = myDeck.GetCard(i); Console.Write(tempCard.ToString()); if (i != 51)

Page 184: مستويات مبتدئ و متوسط -ج2

- 183 - إعداد: المهندس حسام الدين الرز

Console.Write(" , "); else Console.WriteLine(); } Card.isAceHigh = true; Console.WriteLine("Aces are high"); Card.useTrumps = true; Card.trump = Suit.Clup; Console.WriteLine("Clubs are trumps."); Card card1, card2, card3, card4, card5; card1 = new Card(Suit.Clup, Rank.Five); card2 = new Card(Suit.Clup, Rank.Five); card3 = new Card(Suit.Clup, Rank.Ace); card4 = new Card(Suit.Heart, Rank.Ten); card5 = new Card(Suit.Diamond, Rank.Ace); Console.WriteLine("{0}=={1}?{2}", card1.ToString(), card2.ToString(), card1 == card2); Console.WriteLine("{0}!={1}?{2}", card1.ToString(), card3.ToString(), card1 != card3); Console.WriteLine("{0}.Equals({1})?{2}", card1.ToString(), card4.ToString(), card1.Equals(card4)); Console.WriteLine("Cards.Equals({0},{1})?{2}", card3.ToString(), card4.ToString(), Card.Equals(card3, card4)); Console.WriteLine("{0}>{1}?{2}", card1.ToString(), card2.ToString(), card1 > card2); Console.WriteLine("{0}<={1}?{2}", card1.ToString(), card2.ToString(), card1 <= card2); Console.WriteLine("{0}>{1}?{2}", card4.ToString(), card1.ToString(), card4 > card1); Console.WriteLine("{0}>{1}?{2}", card1.ToString(), card4.ToString(), card1 > card4); Console.WriteLine("{0}>{1}?{2}", card5.ToString(), card4.ToString(), card5 > card4); Console.WriteLine("{0}>{1}?{2}", card4.ToString(), card5.ToString(), card4 > card5); } } }

والنتيجة بالشكل التالي:

Page 185: مستويات مبتدئ و متوسط -ج2

- 184 - إعداد: المهندس حسام الدين الرز

(4-11الشكل )

Page 186: مستويات مبتدئ و متوسط -ج2

- 185 - إعداد: المهندس حسام الدين الرز

الخالصة:

Summary:

#Cالتي يمكننا استخدامها لصقل تطبيقاتنا المكتوبة بلغة لقد تناولنا في هذا الفصل العديد من التقنيات

وعلى الرغم من أن التقنيات المشروحة تحتاج لبعض OOPالتي تتعلق بأساليب البرمجة كائنية التوجه و

المجهود لتطبيقها إال انها ستسهل التعامل مع أصنافك والذي يؤدي إلى تبسيط مهمة كتابة شيفرة التطبيقات

ككل.

وع في هذا الفصل استخدامات عديدة ستجد الحقا أنك بحاجة في الغالب الستخدام أحد أشكال لكل موض

المجموعات في أي تطبيق هذا باإلضافة إلى إنشاء مجموعات مخصصة تجعل عملية التعامل مع

عناصرها وأدارتها أسهل وأقوى كما تعرفنا أيضا على المفهرسات للوصول المباشر لعناصر المجموعة.

الستخدامها مع أنواع #Cدثنا أيضا عن التحميل الزائد للعوامل ورأينا أنه يسمح لنا بتعريف عوامل وتح

مختلفة عن األنواع التي تتعامل معها بصورة افتراضية وتحدثنا عن النسخ العميق والذي يمثل أساسا مهما

راضات على إنشاء كائنات االعتلتجنب أحد أكثر األخطاء شيوعا في البرمجة كائنية التوجه وأخيرا تعرفنا

المخصصة وتمرير معلومات مفصلة لمعالج االعتراض.

Page 187: مستويات مبتدئ و متوسط -ج2

- 186 - إعداد: المهندس حسام الدين الرز

خامسالفصل ال

األحداث

OOPهذا هو الفصل األخير في قسم البرمجة كائنية التوجه لهذا الكتاب وبه نكون قد أكملنا حديثنا عن

.Eventsأال وهي األحداث OOP.NETوذلك بتناولنا ألحد أهم التقنيات الشائعة االستخدام في

بالسؤال ماهي األحداث وبعد ذلك سنرى بعض األحداث البسيطة وكيفية التعامل معها ومتى كالمعتادسنبدأ

انتهينا من ذلك سننتقل إلى مواضيع أكثر تقدما حيث سنتعلم كيف ننشئ أحداثنا الخاصة.

بإضافة حدث إليها CardLibraryصناف اللعب مكتبة أ بتشطيبوفي القسم الثاني من هذا الفصل سنقوم

وإضافة إلى ذلك وباعتبار أن هذا الفصل هو المحطة األخيرة التي سننتقل بعدها إلى مواضيع أكثر تقدما

.CardLibraryفإننا سنقوم بإنشاء لعبة ورق بسيطة تستخدم مكتبة األصناف

ما هو الحدث؟

What is an Event?

من الكائنات ويمكن أن thrownتراضات من ناحية عملها فالعتراضات ترمي تتشابه األحداث مع االع

من كاالعتراضاتتحتوي على شيفرة برمجية معينة تنفذ عند رمي االعتراض أي حدوثه األحداث تماما

العمل فهي ترمى )ال ترمى األحداث في الواقع وإنما ترفع أو تقدح( وعند حدوث الحدث فإن آليةناحية

ينة يتم تنفيذها ولكن هناك عدد من االختالفات الهامة بينهما واالخالف األكثر أهمية هو أنه شيفرة مع

لكي تتم معالجتها وبدال من ذلك فإن علينا أن نسجل او نقر try..catchليست لألحداث بنية الكتل

(subscribe) دث وهو ع الحاألحداث وتسجيل األحداث يعني تزويدها بشيفرة معينة يتم تنفيذها عند رف

.event handlerما يسمى بمعالج الحدث

يمكن ان يكون للحدث الواحد العديد من المعالجات المسجلة له والتي سيتم استدعاؤها متى رفع الحدث

إال أنه من النادر أن نجد نف لكائن برفع الحدثيمكن أن يتضمن ذلك معالجات أحداث كجزء من ص

ر الصنف الذي رفع عنده الحدث.معالجات أحداث في أصناف أخرى غي

Page 188: مستويات مبتدئ و متوسط -ج2

- 187 - إعداد: المهندس حسام الدين الرز

والحقيقة البسيطة التي يجب أن تعلمها أن معالجات األحداث ليست أكثر من توابع أو مناهج عادية والتقييد

الوحيد لتابع معالج الحدث هو أنه يجب أن يطابق التوقيع الالزم للحدث )يشتمل ذلك على القيمة المعادة

.delegatesءا من تعريف الحدث ويتم تحديده من خالل المفوضات والبارامترات( يمثل هذا التوقيع جز

مالحظة:

في استخدامها مع األحداث ذاك هو السبب الذي اضطرني delegatesتكمن االستفادة من المفوضات

للتنويه عنها في الفصل السادس من الجزء األول لهذا الكتاب لذا فأنا أتمنى أن تعيد قراءة هذا القسم

كرتك من المفوضات وكيفية االستفادة منها.إلنعاش ذا

واآلن إليك آلية عمل األحداث:

أوال سيقوم التطبيق بإنشاء كائن يستطيع أن يرفع حدثا ما وكمثال لنفترض أن تطبيقنا يمثل تطبيق مراسلة

يرفع نوأن الكائن الذي يقوم بإنشائه يمثل كائن االتصال مع المستخدم البعيد يمكن لكائن االتصال هذا أ

حدثا وذلك عندما تصل رسالة من المستخدم البعيد من خالل هذا االتصال.

(5-1الشكل )

بعد ذلك سيقوم التطبيق بتسجيل الحدث سيقوم تطبيق المراسلة بذلك من خالل تعريف تابع يمكن استخدامه

عالج أن يكون تابع ممع المفوض المحدد من قبل الحدث ويقوم بتمرير مرجع لهذا التابع إلى الحدث يمكن

الحدث هذا منهجا ألي كائن ولنفترض أن كائنا ما يمثل شاشة العرض التي ستعرض الرسائل عند

وصولها.

(5-2الشكل )

Page 189: مستويات مبتدئ و متوسط -ج2

- 188 - إعداد: المهندس حسام الدين الرز

عند رفع الحدث سيلحظ المسجل ذلك وعند وصول الرسالة عبر كائن االتصال سيتم استدعاء منهج معالج

الذي رفع الحدث أن يمرر أية معلومات من خالل الحدث الموجود في كائن العرض ويمكن للكائن

البارامترات وذلك لجعل األحداث متعددة االستعماالت عندئذ يمكن استخدام هذه المعلومات التي أصبحت

تمثل بارامترات لمعالج الحدث لعرضها من خالل كائن العرض.

(5-3الشكل )

توضيح:

غير متناهي من األمثلة التي يمكننا وصف األحداث ليست األحداث بالمنطق الذي يصعب فهمه هناك عدد

بها لنأخذ على سبيل المثال واجهة القيادة في السيارة نالحظ أنها تحتوي على عدد من المؤشرات مثل

الزيت الذي سيؤدي برب الزيت سيتم رفع حدث تسريسالسرعة ومؤشر تسرب الزيت فعند ت مؤشر

لقيادة ويمكن أن يمثل هذا المنبه بصوت متقطع بإضاءة إلى إشعال منبه تسريب الزيت في واجهة ا

LED مثال إن تسريب الزيت هو الحدث وعند رفع هذا الحدث سيتم تنفيذ معالج هذا الحدث ومعالج

أو إصدار الصوت المنبه. LEDالحدث هنا هو الدارة االلكترونية التي ستقوم بإضاءة الـ

استخدام األحداث:

Using Events:

ي هذا القسم كيفية كتابة األحداث ومعالجتها بعد ذلك سننتقل للحديث عن كيفية تعريف وإنشاء سنتعلم ف

أحداثنا المخصصة.

معالجة االحداث:

Handling Events:

Page 190: مستويات مبتدئ و متوسط -ج2

- 189 - إعداد: المهندس حسام الدين الرز

لكي نعالج حدثا ما يجب أن نسجل الحدث وذلك بتوفير تابع معالجة الحدث الذي يتطابق توقيعه مع توقيع

دث لنلق نظرة على مثال يستخدم كائن المؤقت البسيط لرفع األحداث مفوض محدد الستخدامه مع الح

والذي سيؤدي إلى استدعاء معالج الحدث.

بعبارة أخرى لكي نستطيع أن نستخدم الحدث يجب أن نربطه بمعالج حدث له توقيع موافق لتوقيع مفوض

الحدث والصيغة العامة لعملية الربط هي كما يلي:

eventName += new delegateName(eventHandlerFunction)

تطبيق حول معالجة األحداث:

.ConsoleHandlingEventsجديد باسم Consoleقم بإنشاء تطبيق -1

كما يلي: Program.csعدل الشيفرة في الملف -2

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Timers; namespace ConsoleHandlingEvents { class Program { static int counter = 0; static string displayString = "This string will appear one letter at a time."; static void writeChar(object source, ElapsedEventArgs e) { Console.Write(displayString[counter++ % displayString.Length]); } static void Main(string[] args) { Timer myTimer=new Timer (100); myTimer .Elapsed +=new ElapsedEventHandler (writeChar ); myTimer .Start (); Console .ReadLine (); } } }

والحظ ماذا يكتب على الشاشة للخروج من التطبيق اضغط F5نفذ التطبيق بالضغط على زر -3

Enter.

Page 191: مستويات مبتدئ و متوسط -ج2

- 190 - إعداد: المهندس حسام الدين الرز

(5-4الشكل )

كيفية العمل:

How To Work:

يتم تهيئة هذا الكائن System.Timers.Timeإن الكائن المستخدم لرفع األحداث يمثل كائنا من الصنف

سيتم رفع سيل من Startباستخدام المنهج Timerبفترة زمنية محددة بالميلي ثانية وعند بدء عمل كائن

تابع هيئة كائن المؤقت في الاألحداث التي تفصل عن بعضها البعض بالفترة الزمنية المحددة لقد تم ت

Main() وهذا يعني أنه سيتم رفع األحداث عشر مرات في الثانية متى استدعينا المنهج 100بالقيمة

Start():

static void Main(string[] args) { Timer myTimer=new Timer (100);

ويمكنك أن تجد هذا الحدث باإلضافة إلى حدث آخر Elapsedحدثا باسم Timerيمتلك كائن المؤقت

:Object Browserضمن نافذة مستعرض الكائنات Tickاسمه

(5-5الشكل )

Page 192: مستويات مبتدئ و متوسط -ج2

- 191 - إعداد: المهندس حسام الدين الرز

System.Timers.ElapsedEventHandlerأما توقيع معالج الحدث لهذا الحدث فهو من نوع المفوض

يستخدم هذا المفوض مع التوابع NET.ل والذي يمثل واحدا من المفوضات القياسية المعرفة في إطار عم

التي تطابق التوقيع التالي:

Void functionName(object source,ElapsedEventArgs e);

بإرسال مرجع لنفسه في البارامتر األول وكائنا من الصنف Timerيقوم كائن المؤقت

ElapsedEventArgs ن البارامترين اآلن في بارامتره الثاني من األفضل تجنب الحديث عن هذي

وسنتحدث عنهما الحقا.

ونالحظ في شيفرتنا أن هناك منهجا يتطابق مع هذا التوقيع:

static void writeChar(object source, ElapsedEventArgs e) { Console.Write(displayString[counter++ % displayString.Length]); }

وذلك لعرض displayStringو counterهما programيستخدم هذا المنهج حقلين ستاتيكيين للصنف

رمز وحيد على الشاشة وفي كل مرة يستدعى هذا المنهج فيها سيتم طباعة رمز مختلف.

إلضافة معالج هذا =+المهمة التالية هي ربط هذا المنهج مع الحدث وللقيام بذلك فإننا سنستخدم العامل

الحدث على هيئة حالة لمفوض جديد تمت تهيئته بواسطة منهج معالج الحدث:

static void Main(string[] args) { Timer myTimer=new Timer (100); myTimer .Elapsed +=new ElapsedEventHandler (writeChar );

مر الذي يستخدم صيغة غريبة نوعا ما خاصة بالمفوضات بإضافة معالج حدث إلى الئحة يقوم هذا األ

يمكننا إضافة أي عدد من المعالجات إلى هذه Elapsedالتوابع التي سيتم استدعائها عند رفع الحدث

سيتم ثالالئحة طالما أن هذه المعالجات تتوافق مع المعايير المطلوبة للحدث أي تطابق توقيع الحدث حي

استدعاء هذه المعالجات كال بدوره عند رفع الحدث.

هو منهج البدء بتشغيل المؤقت: ()Mainإن ما تبقى من شيفرة األمر

myTimer .Start ();

وبما اننا ال نود من التطبيق أن يتوقف قبل معالجة أية أحداث فإننا سنبقى التطبيق منفذا والطريقة األبسط

تقتضي بطلب إدخال من المستخدم باعتبار أن عملية المعالجة لن Consoleفي تطبيقات لتحقق ذلك

:enterتنتهي قبل أن يدخل المستخدم أي شيء أو أن يضغط على مفتاح

Console .ReadLine (); }

Timerئن المؤقت متقطعة إال أن المعالجة ضمن كا ()Mainوعلى الرغم من أن المعالجة في التابع

والذي سينفذ بتزامن مع ()WriteCharمستمرة فعندما يرفع هذا الكائن األحداث سيتم استدعاء منهجنا

.()Conaole.ReadLineتعليمة

Page 193: مستويات مبتدئ و متوسط -ج2

- 192 - إعداد: المهندس حسام الدين الرز

تعريف األحداث:

Defining Events:

يق ول تطبسوف نحا .واآلن سنتعرف على كيفية إنشاء وتعريف األحداث وبالتالي استخدام أحداثنا الخاصة

Connectionذلك على مثال برنامج المراسلة الذي تحدثنا عنه في بداية هذا الفصل وسنقوم بإنشاء كائن

.Displayيقوم برفع األحداث المعالجة عبر كائن

تطبيق حول تعريف األحداث:

.ConsoleDefiningEventsجديد باسم Consoleقم بإنشاء تطبيق -1

:Connection.csاحفظه ضمن الملف و Connectionأضف صنفا جديدا باسم -2

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Timers; namespace ConsoleDefiningEvents { public delegate void MessageHandler(string messageText); public class Connection { public event MessageHandler MessageArrived; private Timer pollTimer; public Connection () { pollTimer = new Timer(100); pollTimer .Elapsed +=new ElapsedEventHandler (CheckForMessage ); } public void Connect() { pollTimer.Start(); } public void Disconnect() { pollTimer.Stop(); } private void CheckForMessage(object source, ElapsedEventArgs e) { Console.WriteLine("Checking for new messages."); Random random = new Random(); if ((random .Next (9)==0)&&(MessageArrived !=null)) { MessageArrived ("Hello Mum!");

Page 194: مستويات مبتدئ و متوسط -ج2

- 193 - إعداد: المهندس حسام الدين الرز

} } } }

:Display.csواحفظه ضمن الملف Displayأضف صنفا جديدا باسم -3

using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleDefiningEvents { public class Display { public void DisplayMessage(string message) { Console.WriteLine("Message arrived: {0}", message); } } }

كما يلي: Program.csعدل الشيفرة في الصنف -4

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleDefiningEvents { class Program { static void Main(string[] args) { Connection myConnection = new Connection(); Display myDisplay = new Display(); myConnection.MessageArrived += new MessageHandler(myDisplay.DisplayMessage); myConnection.Connect(); Console.ReadLine(); } } }

.F5نفذ التطبيق بالضغط على زر -5

Page 195: مستويات مبتدئ و متوسط -ج2

- 194 - إعداد: المهندس حسام الدين الرز

(5-6الشكل )

كيفية العمل:

How to Work:

بجل العمل في هذا التطبيق فالكائنات من هذا الصنف تستخدم كائن المؤقت Connectionيقوم الصنف

Timer بصورة مشابهة الستخدامه في التطبيق األول حيث تتم تهيئة كائنTimer في منهج البناء للصنف

:Disconnectو ()Connectجين ويأخذ حالة تمكين أو عدم التمكين من خالل المنه

public Connection () { pollTimer = new Timer(100); pollTimer .Elapsed +=new ElapsedEventHandler (CheckForMessage ); } public void Connect() { pollTimer.Start(); } public void Disconnect() { pollTimer.Stop(); }

ضمن منهج البناء تماما كما قمنا بذلك في التطبيق Elapsedالحظ أيضا أننا قمنا بتسجيل معالج للحدث

عدل عشر مرات في الثانية ولكن قبل أن فسوف يرفع بم ()CheckMessageالسابق أما منهج الحدث

نتناول شيفرة هذا المنهج سنلقي نظرة على تعريف الحدث.

Page 196: مستويات مبتدئ و متوسط -ج2

- 195 - إعداد: المهندس حسام الدين الرز

قبل أن نعرف حدثا علينا أن نعرف نوع مفوض الستخدامه مع الحدث أي أن نوع المفوض هو الذي يحدد

وض وذلك فتوقيع معالج الحدث الذي يطابق الحدث ولكي نعرف مفوضا فإننا سنستخدم صيغة تعريف الم

وذلك لتمكين الوصول لهذا ConsoleDefiningEventsبتعريفه كمفوض عام داخل فضاء األسماء

النوع من خالل الشيفرة الموجودة خارج الصنف.

namespace ConsoleDefiningEvents {

messageText); string(MessageHandler void delegate public

أي ال يعيد Voidوهو مفوض من نوع MessageHandlerنا االسم لقد أطلقنا على مفوض الحدث ه

سنستخدم هذا البارامتر لتمرير الرسالة المستلمة من كائن Stringقيمة وله بارامتر واحد من نوع

Connection إلى كائنDisplay.

حدث لومتى تم تعريف المفوض أو كان هناك مفوض مناسب موجود مسبقا فإنه يمكننا عندئذ تعريف ا

:Connectionوذلك كعضو في الصنف

public class Connection {

MessageArrived; MessageHandler event public

MessageHandlerوكما تالحظ فإن صيغة تعريف الحدث بسيطة فقد قمنا بتسجيل الحدث وفقا للمفوض

دد أن التعريف هو تعريف الحدث.تح eventوالكلمة المفتاحية MessageArrivedوباسم

ومتى صرحنا عن الحدث بهذه الطريقة فإنه يمكننا رفع هذا الحدث بمجرد استدعاءه باسمه كما لو كنا

نستدعي منهجا عاديا ووفقا للتوقيع المحدد بواسطة المفوض على سبيل المثال يمكننا رفع الحدث كما يلي:

);"s a messageThis i"MessageArrived (

وإذا كان المفوض المعرف لهذا الحدث ال يحوي أية بارامترات فإننا سنرقع الحدث بمجرد كتابة:

MessageArrived ();

وبصورة بديلة فإنه يمكننا تعريف أكثر من بارامتر ضمن مفوض الحدث والذي سيتطلب منا تحديد هذه

البارامترات لرفع الحدث.

()CheckForMessageفي هذا التطبيق هو المنهج MessageArrivedالذي يرفع الحدث الجزء

وشيفرته كما يلي:

private void CheckForMessage(object source, ElapsedEventArgs e) { Console.WriteLine("Checking for new messages."); Random random = new Random(); if ((random .Next (9)==0)&&(MessageArrived !=null)) { MessageArrived ("Hello Mum!"); }

Page 197: مستويات مبتدئ و متوسط -ج2

- 196 - إعداد: المهندس حسام الدين الرز

}

م رفع الحدث عندما ومن ثم سيت 9و 0لتوليد رقم عشوائي بين Randomلقد استخدمنا هنا كائنا من نوع

هذا يحاكي اقتراع االتصال لرؤية قيما إذا وصلت إن %10والذي يمكن أن يحدث بنسبة 0يكون الرقم هو

رسالة ام ال.

MessageArrivedالحظ اننا استخدمنا منطقا إضافيا هنا فنحن سنرفع الحدث إذا كانت قيمة التعبير

!=null جالت؟" فإن لم يكن هناك أية مفوضات مسجلة لهذا الحدث ومعنى هذا التعبير "هل للحدث أية مس

وبالتالي لن تكون هناك أية إشارة لرفع الحدث. nullسيعيد القيمة MessageArrivedفإن

()DisplayMessageويتضمن منهجا واحدا Displayيسمى الصنف الذي سيستجيب للحدث بالصنف

معرفا كما يلي:

public class Display { public void DisplayMessage(string message) { Console.WriteLine("Message arrived: {0}", message); }

}

وبالتالي يمكننا استخدام هذا المنهج MessageHandlerيطابق توقيع هذا المنهج توقيع المفوض في

.MessageArrivedكاستجابة للحدث

Connectionوالذي يقوم بتهيئة حالة من الصنف ()Mainكل ما تبقى لدينا اآلن هو شيفرة المنهج

مع المنهج Connectionللصنف MessageArrivedومن ثم يقوم بربط الحدث Displayو

DisplayMessage للصنف وفقا للمفوضMessageHandler ومن ثم يقوم باستدعاء المنهج

Connect() .لبدء عمل المؤقت

static void Main(string[] args) { Connection myConnection = new Connection(); Display myDisplay = new Display(); myConnection.MessageArrived += new MessageHandler(myDisplay.DisplayMessage); myConnection.Connect(); Console.ReadLine();

}

مؤقتا ()Mainوذلك إليقاف تنفيذ المنهج ()Console.ReadLineومجددا فقد قمنا باستدعاء المنهج

كي نتمكن من رفع االحداث قبل إنهاء التطبيق.

اض:معالجات األحداث متعدد األغر

Multi-Purpose Event Handlers:

Page 198: مستويات مبتدئ و متوسط -ج2

- 197 - إعداد: المهندس حسام الدين الرز

بارامترين وهما من النوع الذي سنجده غالبا في معالجات Timer.Elapsedيتضمن توقيع الحدث

األحداث:

البارامترsource من النوعobject .وهو مرجع للكائن الذي رفع الحدث

البارامترe من النوعElapsedEventArgs من قبل الحدث. ويتضمن البارامترات المرسلة

هو حاجتنا في أغلب األحيان إلى استخدام معالج حدث وحيد objectإن سبب وجود بارامتر من النوع

ألكثر من حدث بحيث يرفع هذا الحدث من أكثر من كائن وبالتالي ستنفذ شيفرة محددة ضمن معالج الحدث

وفقا للكائن الذي رفع الحدث.

ر التطبيق السابق.لشرح ذلك بطريقة أفضل سنقوم بتطوي

تطبيق حول استخدام معالج الحدث متعدد األغراض:

.ConsoleMulti-PurposeEventHandlersجديد باسم Consoleقم بإنشاء تطبيق -1

من المشروع Program.csو Connection.csو Display.csانسخ شيفرة األصناف -2

ConsoleDefiningEvents ل فضاء األسماء إلى إلى المشروع الحالي وال تنسى أن تعد

ConsoleMulti-PurposeEventHandlers.

.MessageArrivedEventArgsأضف صنفا جديدا باسم -3

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleMulti_PurposeEventHandlers { public class MessageArrivedEventArgs:EventArgs { private string message; public string Message { get { return message; } } public MessageArrivedEventArgs () { message = "No message sent."; } public MessageArrivedEventArgs (string newMessage) { message = newMessage; } } }

Page 199: مستويات مبتدئ و متوسط -ج2

- 198 - إعداد: المهندس حسام الدين الرز

كما يلي: Connection.csعدل شيفرة الملف -4

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Timers; namespace ConsoleMulti_PurposeEventHandlers { public delegate void MessageHandler(Connection source, MessageArrivedEventArgs e); public class Connection { public event MessageHandler MessageArrived; private string name; public string Name { get { return name; } set { name = value; } } private Timer pollTimer; public Connection() { pollTimer = new Timer(100); pollTimer.Elapsed += new ElapsedEventHandler(CheckForMessage); } public void Connect() { pollTimer.Start(); } public void Disconnect() { pollTimer.Stop(); } private void CheckForMessage(object source, ElapsedEventArgs e) { Console.WriteLine("Checking for new messages."); Random random = new Random(); if ((random.Next(9) == 0) && (MessageArrived != null)) { MessageArrived(this , new MessageArrivedEventArgs ("Hello Mum!.")); } }

Page 200: مستويات مبتدئ و متوسط -ج2

- 199 - إعداد: المهندس حسام الدين الرز

} }

كما يلي: Display.csعدل الملف -5

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleMulti_PurposeEventHandlers { public class Display { public void DisplayMessage(Connection source, MessageArrivedEventArgs e) { Console.WriteLine("Message arrived: {0}", source .Name ); Console.WriteLine("Message Text: {0}", e.Message); } } }

كما يلي: Program.csعدل شيفرة الملف -6

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleMulti_PurposeEventHandlers { class Program { static void Main(string[] args) { Connection myConnection1 = new Connection(); myConnection1.Name = "First connection."; Connection myConnection2 = new Connection(); myConnection2.Name = "Second connection."; Display myDisplay = new Display(); myConnection1.MessageArrived += new MessageHandler(myDisplay.DisplayMessage); myConnection2.MessageArrived += new MessageHandler(myDisplay.DisplayMessage); myConnection1.Connect(); myConnection2.Connect(); Console.ReadLine(); } } }

Page 201: مستويات مبتدئ و متوسط -ج2

- 200 - إعداد: المهندس حسام الدين الرز

نفذ التطبيق: -7

(6-6الشكل )

كيفية العمل:

How to Work:

كائن دث وذلك بإرسال مرجع لهذا اليمكننا تخصيص استجابة معالج الحدث تبعا للكائنات التي رفعت الح

الواحد من بارامترات معالج الحدث فهذا المرجع يمكننا من الوصول إلى الكائن األصلي بما يتضمن من

خصائص ومناهج.

كما يقوم System.EventArgsالموجودة في الصنف الذي يرث من الصنف البارامتراتوبإرسال

زويد معالج الحدث بأية معلومات إضافية ضرورية على فإنه يمكننا ت ElapsedEventArgsالصنف

.MessageArrivedEventArgsضمن الصنف Messageهيئة بارامترات مثل البارامتر

وباإلضافة إلى ذلك فإن هذه البارامترات ستستفيد من تعددية األشكال حيث يمكننا تعريف معالج حدث

كما يلي مثال: MessageArrivedللحدث

public void DisplayMessage(object source,EventArgs e) { Console.WriteLine("Message arrived: {0}", ((Connection )source ).Name ); Console.WriteLine("Message Text: {0}", ((MessageArrivedEventArgs )e).Message );

}

كما يلي: Connection.csوتعديل تعريف المفوض في الملف

Page 202: مستويات مبتدئ و متوسط -ج2

- 201 - إعداد: المهندس حسام الدين الرز

e); EventArgssource, object(MessageHandler void delegate public

أكثر مرونة فمعالج ()DisplayMessageسينفذ التطبيق تماما كالسابق ولكن بهذه الطريقة أصبح المنهج

مثال سنحتاج عندها إلى تعديل Timer.Elapsedن يستخدم ألحداث أخرى مثل الحدث الحدث هذا يمكن أ

لكي نتمكن من استقبال الحدين.متراته اشيفرة المنهج وبار

القيم المعادة من معالجات األحداث:

Return Values and Event Handlers:

من الممكن أن Voidأنها من النوع إن جميع معالجات األحداث التي رأيناها إلى اآلن ال تعيد قيما أي

تعيد األحداث قيما معينة لحدث ما إال أن هذا قد يقود إلى مشاكل ويعود سبب ذلك إلى أن الحدث الواحد

يمكن أن يؤدي إلى استدعاء عدة معالجات أحداث فإن أعادت جميع معالجات األحداث قيمة ما فإننا لن

عيدت من معالج حدث معين.نتمكن من تحديد القيمة الحقيقية التي أ

هي لحدث وتلكيتعامل النظام مع هذه المشكلة بالسماح لنا بوصول إلى القيمة األخيرة المعادة من معالج ا

خر معالج حدث مسجل للحدث.القيمة المعادة من آ

قد تحتاج لسبب ما إلى إعادة قيمة من معالج الحدث إال أن هذا نادر جدا لذا فإنني أنصحك باستخدام

وهذا يشمل أيضا تجنب استخدام voidالتي تستخدم الكلمة يأ معالجات األحداث التي ال تعيد أية قيمة

مع معالجات األحداث. outبارامترات الخرج

توسعة واستخدام مكتبة أصناف أوراق اللعب:

Expanding and Using CardLibrary:

ث سنقوم بإضافة أحداث مكتبة أصناف أوراق واآلن وبعد ان تعرفنا على كيفية تعريف واستخدام األحدا

وكما في السابق سوف نقوم CardLibraryاللعب التي طورناها في الفصل السابق والتي كانت باسم

إلى CardLibraryوتذكر تغيير فضاء األسماء من CardLibrary2بإنشاء مشروع جديد باسم

CardLibrary2 سيتم رفع هذا الحدث عند الحصول على سنقوم بإضافة واحد إلى مكتبة األصناف و

وسيسمى هذا الحدث بـ ()GetCardباستخدام المنهج Deckاألخير في كائن Cardكائن

LastCardDrawn بمعنى أوضح سيتم رفع هذا الحدث عندما نسحب أو نلعب الورقة األخيرة في

اق اللعب بشكل تلقائي أما مجموعة أوراق اللعب سيسمح هذا الحدث للمسجالت بإعادة خلط مجموعة أور

فيجب أن يزود بمرجع لكائن LastCardDrawnHandlerالمفوض المستخدم مع هذا الحدث وهو باسم

Deck بحيث يمكن الوصول إلى المنهجShuffle() :من أي معالج حدث وذلك كما يلي

using System; using System.Collections.Generic; using System.Linq;

Page 203: مستويات مبتدئ و متوسط -ج2

- 202 - إعداد: المهندس حسام الدين الرز

using System.Text; using System.Threading.Tasks; namespace CardLibrary2 { public delegate void LastCardDrawnHandler(Deck currentDeck); public class Deck:ICloneable { public object Clone() { Deck newDeck = new Deck((Cards)cards.Clone()); return newDeck; } private Deck (Cards newCards) { cards = newCards; } private Cards cards = new Cards(); public Deck () { // Cards = new Card[52]; for (int suitVal=0;suitVal <4;suitVal ++) { for (int rankVal=1;rankVal <14;rankVal ++) { cards.Add1(new Card((Suit)suitVal, (Rank)rankVal)); } } } public Deck (bool isAceHigh):this() { Card.isAceHigh = isAceHigh; } public Deck (bool useTrumps,Suit trump):this() { Card.useTrumps = useTrumps; Card.trump = trump; } public Deck (bool isAceHigh,bool useTrumps,Suit trump):this() { Card.isAceHigh = isAceHigh; Card.useTrumps = useTrumps; Card.trump = trump; } public event LastCardDrawnHandler LastCardDrawn; public Card GetCard(int cardNum) { if (cardNum >= 0 && cardNum <= 51) { if ((cardNum ==51)&&(LastCardDrawn !=null )) LastCardDrawn(this ); return cards[cardNum]; }

Page 204: مستويات مبتدئ و متوسط -ج2

- 203 - إعداد: المهندس حسام الدين الرز

else throw new CardOutOfRangeException((Cards)cards.Clone()); } public void Shuffle() { //Card[] newDeck = new Card[52]; Cards newDeck = new Cards(); bool[] assigned = new bool[52]; for (int i=0;i<52;i++) { int sourceCard = 0; bool foundCard = false; Random sourceGen = new Random(); while (foundCard ==false ) { sourceCard = sourceGen.Next(52); if (assigned[sourceCard] == false) foundCard = true; } assigned [sourceCard ]=true ; newDeck.Add1(cards[sourceCard]); } cards = newDeck; } } }

واآلن سنرى كيف سنستخدم هذا الحدث. Deckتلك هي الشيفرة الضرورية إلضافة حدث للصنف

:CardLibrary2تطبيق زبون للعبة ورق اللعب باستخدام مكتبة األصناف

A Card Game Clint for CardLibrary2:

سيكون من المخجل عدم CardLibrary2بعد أن استغرقنا كل هذا الوقت في إنشاء مكتبة األصناف

علينا إن NET.وإطار عمل #Cاستخدامها وقبل أن ننهي هذا القسم المتعلق بالبرمجة كائنية التوجه في

النسبة عب التي أصبحت مألوفة بنلهو قليال بكتابة أساسيات تطبيق لعبة ورق تستخدم أصناف أوراق الل

لنا.

ومن ثم CardLibrary2زبون إلى الحل Consoleوكما في الفصول السابقة فإننا سنضيف تطبيق

وسنجعل هذا التطبيق مشروع نقطة البدء CardLibrary2سنضيف إلى هذا التطبيق مرجعا للمشروع

في Playerإنشاء صنف جديد باسم كبداية سنقوم ب 2ConsoleCardClientسيسمى هذا التطبيق بـ

سيحتوي هذا الصنف على حقل CardLibrary2وذلك ضمن المشروع Player.csملف جديد باسم

وخاصيتان للقراءة فقط nameوحقل سلسلة نصية خاص باسم handباسم Cardsخاص من نوع

.handو nameستمثل هاتين الخاصيتين واجهة للحقلين PlayHandالخاصية

وسنقوم بإنشاء منهج بناء Privateم بإخفاء منهج البناء االفتراضي وذلك باستخدام محدد الوصول سنقو

.Playerفي كائنات Nameغير افتراضي يتقبل قيمة أولية للخاصية

Page 205: مستويات مبتدئ و متوسط -ج2

- 204 - إعداد: المهندس حسام الدين الرز

إذا كانت Trueسيعيد هذا المنهج القيمة ()HasWonوأخيرا سنقوم بتوفير منهج يعيد قيمة منطقية باسم

في يد الالعب من الفئة نفسها وهو شرط للفوز بسيط إال أننا لن نخوض في ألعاب أكثر جميع األوراق

تعقيدا.

هي كما يلي: Player.csإن شيفرة الملف

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using CardLibrary2; namespace CardLibrary2 { public class Player { private Cards hand; private string name; public string Name { get { return name; } } public Cards PlayHand { get { return hand; } } private Player () { } public Player (string newName) { name = newName; hand = new Cards(); } public bool HasWon() { bool won = true; Suit match = hand[0].suit; for (int i=1;i<hand .Count ;i++) { won &= hand[i].suit == match; } return won; } }

Page 206: مستويات مبتدئ و متوسط -ج2

- 205 - إعداد: المهندس حسام الدين الرز

}

قم بحفظ هذا الصنف ضمن Gameبعد ذلك سنعرف صنفا يقوم بمعالجة لعبة الورق وسنسميه بالصنف

.CardLibrary2ضمن المشروع Game.csالملف

لهذا الصنف أربعة حقول خاصة:

الحقلplayDeck وهو من النوعDeck .ويحتوي على مجموعة أوراق اللعب المستخدمة

ل الحقcurrentCard ويمثل قيمة من النوعint تستخدم كمؤشر للورقة التالية في مجموعة

أوراق اللعب التي سيتم سحبها.

الحقلplayers ويمثل مصفوفة كائناتplayer .تمثل الالعبين في اللعبة

الحقلdiscardedCards ويمثل مجموعةCards لألوراق التي تم رميها عزلها ولم تتم إعادتها

لى مجموعة أوراق اللعب.إ

المخزن Deckيقوم منهج البناء االفتراضي في الصنف بتهيئة وإعادة فرز مجموعة أوراق اللعب كائن

كمؤشر للورقة األولى في المجموعة currentCardثم يقوم بإعداد المتحول playDeckضمن المتحول

وربطه بالمنهج PlayDeck.LastCardDrawnومن ثم يقوم بتسجيل معالج الحدث 0بالقيمة

Reshuffle() يقوم هذا المنهج بإعادة خلط أوراق اللعب بطريقة عشوائية باإلضافة إلى األوراق التي تم

وذلك currentCardضمن 0ويقوم بوضع القيمة discardedCardsرميها الموجودة ضمن المجموعة

استعدادا للبدء بقراءة أوراق اللعب من المجموعة الجديدة.

الذي يستخدم إلعداد الالعبين في اللعبة ()setPlayersمنهجين مساعدين أيضا Gameيتضمن الصنف

وذلك إلنشاء أوراق اللعب في اليد أي ()DealHandsوالمنهج playersعلى شكل مصفوفة من كائنات

عبين ح بين الاألوراق التي يحملها كل العب بحيث يحمل كل العب سبع أوراق أما عدد الالعبين فيتراو

إلى سبعة العبين كحد أقصى.

والذي يتضمن منطق اللعبة وسنتحدث عن هذا المنهج الحقا ()playGameوأخيرا فإن هناك المنهج

:Game.csوإليك شيفرة الملق

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using CardLibrary2; namespace CardLibrary2 { public class Game { private int currentCard; private Deck playDeck; private Player[] players; private Cards discardedCards;

Page 207: مستويات مبتدئ و متوسط -ج2

- 206 - إعداد: المهندس حسام الدين الرز

public Game () { currentCard = 0; playDeck = new Deck(true); playDeck.LastCardDrawn += new LastCardDrawnHandler(Reshuffle); playDeck .Shuffle (); } private void Reshuffle(Deck currentDeck) { currentDeck.Shuffle(); discardedCards = new Cards(); currentCard = 0; } public void SetPlayers(Player [] newPlayers) { if (newPlayers.Length > 7) throw new ArgumentException("A maximum of 7 players" + "may play this game."); if (newPlayers.Length < 2) throw new ArgumentException("A minimum of 2 players" + "may play this game."); players = newPlayers; } private void DealHands() { for (int p=0;p<players .Length ;p++) { for (int c=0;c<7;c++) { players[p].PlayHand.Add1(playDeck.GetCard(currentCard++)); } } } public int PlayGame() { //code to follow } } }

والذي سيقوم بتهيئة وتنفيذ اللعبة يقوم هذا المنهج بما ()Mainعلى التابع Program.csيشتمل الصنف

يلي:

.عرض مقدمة اللعبة

عدد االعبين. حث المستخدم على إدخال

إعداد مصفوفة كائناتplayers .وفقا لعدد الالعبين الذي تم إدخاله

حث المستخدم على إدخال أسماء الالعبين وذلك لتخزين هذه األسماء ضمن كائناتplayer في

المصفوفة.

إنشاء كائن اللعبةGame وإنشاء الالعبين باستخدام المنهجSetPlayers().

Page 208: مستويات مبتدئ و متوسط -ج2

- 207 - إعداد: المهندس حسام الدين الرز

عادة من المنهج استخدام القيمة المplayGame() لعرض الالعب الرابح تمثل هذه القيمة دليل

.playersالالعب الفائز في مصفوفة كائنات

إن شيفرة هذا التابع هي كما يلي:

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using CardLibrary2; namespace ConsoleCardClient2 { class Program { static void Main(string[] args) { //Display introduction. Console.ForegroundColor = ConsoleColor.Cyan; Console.WriteLine("karliCards:a new and exciting card game."); Console.WriteLine("to win you must have 7 cards of the same" + "suit in your hand."); Console.WriteLine(); // prompet for number of player. bool inputOK = false; int choice = -1; do { Console.ForegroundColor = ConsoleColor.Red; Console.WriteLine("how many players (2-7)?"); string input = Console.ReadLine(); try { //Attempt to convert input a vallid number of player. choice = Convert.ToInt32(input); if ((choice >= 2) && (choice <= 7)) inputOK = true; } catch { //ignore failed conversions just continue prompting } } while (inputOK == false); Player[] players = new Player[choice]; for (int p = 0; p < players.Length; p++) { Console.ForegroundColor = ConsoleColor.Green; Console.WriteLine("player {0},enter your name:", p + 1); string playerName = Console.ReadLine(); players[p] = new Player(playerName); }

Page 209: مستويات مبتدئ و متوسط -ج2

- 208 - إعداد: المهندس حسام الدين الرز

Game newGame = new Game(); newGame.SetPlayers(players); int whoWon = newGame.PlayGame(); Console.WriteLine("{0} has won the game!", players[whoWon].Name); } } }

يل دث هنا عن التفاصلن نتح للتطبيقوالذي يمثل الجسم الرئيسي ()PlayGameن حان دور المنهج واآل

في الحقيقة إن هذه الشيفرة ليست معقدة بالدرجة التي تتصورها وستتمكن من فهمها بسهولة.لهذا المنهج ف

ب ورقة من اوراقه السبعة على الطاولة تبدأ اللعبة بعد عرض أوراق كل العب وحيث يقوم كل العب بقل

عندئذ يمكن لالعب آخر اختيار هذه الورقة المقلوبة او سحب ورقة جديدة من مجموعة األوراق وبعد أن

يسحب الالعب ورقة ينبغي عليه أن يرمي ورقة بالمقابل وذلك إما باستبدال الورقة الموجودة على الطاولة

ته إذا اختار تلك الورقة المقلوبة على الطاولة أو بوضع الورقة التي بواحدة أخرى من األوراق التي بحوز

على الطاولة أي إضافة ورقة سابقة على الطاولة إلى المجموعة ال يريدها فوق الورقة الموجودة

discardedCards.

د على والمبدأ هنا يعتم Cardإن إحدى النقاط الرئيسية في هذه الشيفرة تكمن في طريقة معالجة كائنات

تعريف أوراق اللعب بأنواع مرجعية بدال من أنواع قيمة وهو أمر يجب أن يكون واضحا بالنسبة إليك

يمكن ان يكون موجودا في أماكن عديدة في الوقت نفسه فسيكون هناك مؤشر إلى Cardفنالحظ ان كائن

ع للورقة نفسها في يد ( باإلضافة إلى مرجDeck)كائن ورقة اللعب الموجودة في مجموعة أوراق اللعب

( وضمن األوراق التي تم رميها ضمن المجموعة Playerلكائنات handالالعب )ضمن حقول

discardedCards كائن( ويمكن الن تكون الورقة الموجودة على الطاولة حالياPlayCard إن )أيضا

تي سيتم سحبها من استخدام نوع مرجعي لكل ورقة لعب سيسهل من عملية تتبع األوراق فالورقة ال

مجموعة أوراق اللعب سيتم قبولها فقط إذا لم تكن في يد أي العب ولم تكن على الطاولة ولم تكن أيضا

ضمن مجموعة األوراق التي تم رميها.

هي كما يلي: ()PlayGameوشيفرة المنهج

public int PlayGame() { if (players == null) return -1; DealHands(); bool GameWon = false; int currentPlayer; Card playCard = playDeck.GetCard(currentCard++); do { for (currentPlayer = 0; currentPlayer < players.Length ; currentPlayer++) { Console.ForegroundColor = ConsoleColor.Yellow; Console.WriteLine("{0} 's turn", players[currentPlayer].Name);

Page 210: مستويات مبتدئ و متوسط -ج2

- 209 - إعداد: المهندس حسام الدين الرز

Console.WriteLine("current hand:"); foreach (Card card in players[currentPlayer].PlayHand) { Console.WriteLine(card); } Console.WriteLine("card in play:{0}", playCard); bool inputOk = false; do { Console.ForegroundColor = ConsoleColor.Blue; Console.WriteLine("press T to take card in play or D" + " to draw:"); string input = Console.ReadLine(); if (input.ToLower() == "t") { Console.WriteLine("Drawn: {0}", playCard); players[currentPlayer].PlayHand.Add1(playCard); inputOk = true; } if (input.ToLower() == "d") { Card newCard; bool cardIsInPlayerHand; do { newCard = playDeck.GetCard(currentCard++); cardIsInPlayerHand = false; foreach (Player testPlayer in players) { cardIsInPlayerHand = testPlayer.PlayHand. Contains1(newCard); } } while (cardIsInPlayerHand); Console.WriteLine("Drawn: {0}", newCard); inputOk = true; } } while (inputOk == false); Console.WriteLine("New hand:"); for (int i = 0; i < players[currentPlayer]. PlayHand.Count; i++) { Console.WriteLine("{0}: {1}", i + 1, players[currentPlayer].PlayHand[i]); } inputOk = false; int choice = -1; do { Console.WriteLine("choose card to discard:"); string input = Console.ReadLine(); try {

Page 211: مستويات مبتدئ و متوسط -ج2

- 210 - إعداد: المهندس حسام الدين الرز

choice = Convert.ToInt32(input); if ((choice > 0) && (choice <= 8)) inputOk = true; } catch { } } while (inputOk == false); playCard = players[currentPlayer].PlayHand[choice - 1]; players[currentPlayer].PlayHand.RemoveAt(choice - 1); Console.WriteLine("Discarding:{0}", playCard); Console.WriteLine(); GameWon = players[currentPlayer].HasWon(); if (GameWon == true) break; } } while (GameWon == false); return currentPlayer;

}

واستمتع باللعبة وتأكد من استيعابك لمنطقها وكيفية تطبيقه بصورة برمجية F5واآلن اضغط على زر

في المنهج السابق.

Page 212: مستويات مبتدئ و متوسط -ج2

- 211 - إعداد: المهندس حسام الدين الرز

الخالصة:

Summary:

أال وهو األحداث #Cلقد تعلمنا في هذا الفصل أحد أهم المواضيع المرتبطة بالبرمجة كائنية التوجه في لغة

م من أن هذا الموضوع يحتاج إلى تفصيل أكبر مما قدمناه في هذا الفصل إال أن الشيفرة التي وعلى الرغ

طرحناها هنا تمكنك من استيعاب معظم ما يدور حول األحداث وكيفية معالجتها سوف تجد األحداث كثيرا

إطار عمل ت فيفي الفصول القادمة وستجد أنها تمثل جزءا ال يمكنك التخلي عنه أثناء تصميم التطبيقا

.NET

دث ما عند رفع ح هاوباإلضافة إلى أساسيات األحداث وكيفية إنشاء معالجات األحداث وتسجيلها لتنفيذ

فقد تناولنا تطبيقا كامال يبين كيفية استخدام األحداث وذلك باستخدام مكتبة أصناف مجموعة أوراق اللعب

ن ضمن لعبة الورق ومكتبة األصناف واستخدمنا معظم ما تعلمناه منذ بداية الكتاب وحتى اآل

CardLibrary.