رمز فتح مرحلة هذا الدرس في تطبيق طورني : MRE

تَعلُمُ البرمجةِ للمبتدئينَ كلياً بواسطةِ #C –  مواضيع متقدمة حول الفئات Classes

مواضيع متقدمة حول الفئات Classes :

ما هو الـ Garbage Collector ، و ما هي دلالات كلمة  new التس نستخدمها عند الاشتقاق من الكلاس الرئيسي ، ما هي الـ Static ، و ما هو الـ Constructer الدالة التي تقوم بتنفيذ أسطر برمجية عند الاستنساخ من كلاس رئيسي ؟

هذا كله سنتعرف عليه في هذا الدرس بعد الفاصل إن شاء الله .

 

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

بسم الله الرحمن الرحيم ، في الدرس الماضي تحدثنا عن مدخل إلى البرمجة كائنية التوجه OOP و التي هي اختصار لـ Object Oriented Programming ، و تعلمنا كيفية بناء Class ، و التي  أصبحنا نعلم أنه يمكن ترجمة الـ Class إلى العربية بكلمة ” الفئة ” ، و تكلمنا كيف يمكنك بناء فئات أو Classes تحوي معلومات مشتركة ، و رأينا كيف يمكننا تعريف الـ Property  و رأينا كيف يمكننا استنساخ Object من هذا الـ Class

في هذا الدرس سنتكلم إن شاء الله عن أمور و تطبيقات إضافية للـ Classes و الـ Method نفسها ، و سنتكلم عن مفهوم الـ Object نفسه ، فما هو مفهوم الـ Object عندما نقوم باشتقاقه من الـ  Class ، و كيف يتم تخزينه و استدعائه ، و ماهي حلقة الوصل بينه و بين الذاكرة ، سيتم مناقشة هذا في القسم الأول من الدرس ، و سنراجع معاً ما هو دور الـ Dot Net Runtime في عملية إدارة الذاكرة .

 

تكلمنا في البداية أن الـ Dot Net له أقسام معينة و هو مسؤول عن تحويل أمور معينة و تعرفنا على Common Language Runtime و اختصارها CLR ، حيث سنأخذ فكرةً عنها ، و كيف ستساعدنا هذه الأمور في انتاج برامج قوية قادرة على إدارة الـ Data  ، و قادرة على إدارة عمل البرنامج بشكل افضل .

و سنتكلم أيضاً عن شيء يسمّى Constructer  ؛ و هو Method خاصة يتم استدعاؤها عندما يتم استنساخ Object من الـ Class ، و سنتكلم أيضاً عن معنى كلمة Static ، و التي رافقتنا من أول درس إلى هذا الدرس .

 

كالعادة نقوم بإنشاء مشروع جديد بلغة c# من النوع Console App

في الدرس الماضي تكلمنا عن المكان المناسب لتعريف الـ Class ، حيث قلنا أنه يجب أن يكون موازياً للـ Class الرئيسي ، في المنطقة الواقعة داخل الحافظة الرئيسية namespace  ؛ و التي تحتوي على مجموعة من الكلاسات .

 

في البداية هناك قضية أودُّ التنويه لها ، و هي إمكانية كتابة Class ضمن كلاس آخر ، أو ما يعرف بالـ Nested Class ، لكننا لن نتعمق في هذا الموضوع ، و سنتعلم الـ Class بمفهومه البسيط ، و الذي يمكن استخدامه في أي مكان .

إذا لكتابة Class جديد نذهب لأي منطقة موازية لأي كلاس آخر ، و يمكن كتابة الـ Class فوق الكلاس الرئيسي أو تحته ، فلا مشكلة في هذه النقطة ، إذا سننتقل إلى السطر رقم 15 ، و أقوم بكتابة نفس الـ Class الذي تعاملنا معه في الدرس الماضي .

لاحظوا أن هذا الـ Class المسمّى Car يحتوي على 4 خصائص ، كل منها لديه نوع خاص ، فالشركة المصنعة لديها النوع string و سنة التصنيع من النوع int .

اشتقاق Object من Class معين :

و لاشتقاق Object من هذا الـ Class تعرفنا أنه يمكننا كتابة اسم الـ Class الرئيسي أولاً ، ثم نقوم بتعريف متغير جديد myCar على سبيل المثال ، ثم وضع إشارة المساواة ، ثم new ثم اسم الكلاس مرة أخرى ، مع وضع اقواس () و الفاصلة المنقوطة ، حيث قمنا هكذا بجعل هذا الـ Object مستنسخاً من الـ Class الذي اسمه Car ؛ لاحظوا في هذه الجملة قمنا بتعريف متغير نستطيع أن نقول عنه أنه Object من النوع Car ، و لاحظوا أنه عندما قمت بوضع كلمة new فهناك عمليات موجودة خلف هذه الكلمة .

 

ما معنى كلمة new ؟

عندما تقوم بعملية استنساخ أو بناء مجسم جديد ، فأنت فعلياً قمت بربط هذا المجسم و معلوماته بمكان معين في الذاكرة ، Address  معين في الـ Memory ، و هذا المكان في البداية هو مكان فارغ عندما قمنا بكتابة Car myCar ، و لكن عندما قمنا بكتابة new Car قمت بإضافة المعلومات الأساسية التي يتكون منها هذا الكائن ، فعندنا في البداية كائن قمنا بتعريفه فارغاً و لكن عندما قمنا بعملية ربطه بكلمة new قام فعلياً بعملية انشاءه و تهيئته بالشكل الذي قمنا بكتابته في الـ Class ، و سنتعرف بعد قليل على ميزة خاصة بعملية الـ new Car الموجودة هنا .

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

 

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

يمكننا أيضاً إضافة Object آخر من نفس النوع Car و جعله يشير إلى نفس المكان الموجود في الذاكرة ، بمعنى … سنقوم بإضافة متغير جديد باسم myOtherCar ، و لاحظوا أنني في هذه الجملة قمت بتعريف Object لكنه فارغ ، لا يشير إلى مكان في الذاكرة ، أو ليس له محتويات يشير إليها ، فإن قلت له في السطر التالي ، myOtherCar = myCar ، ففعلياً في هذا السطر جعلت الـ Object يشير إلى المكان الذي يشير إليه myCar

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

لدينا البالون الذي اتفقنا سابقاً أن له خيطاً و اعتبرناه myCar ، بالجملة التي كتبناها قمنا بعمل خيط آخر ، و الذي هو myOtherCar و الخيطين يشيران إلى نفس البالون ، و لاحظوا أنني لم أقم بإسناد أي قيمة للمتغير ، بل جعلته مساوياً للـ Object السابق .

بمعنى أنني إذا قمت بعمل طباعة الجملة التالية ، فعندها سيقوم بطباعة Black ، على الرغم من أنني قمت بطباعة myOtherCar  و ليس myCar ، إذا فعليا في المتغير myOtherCar قمت بالإشارة إلى نفس البالون الذي يحمله الـ myCar و لتتأكدوا أكثر أن الاثنان مرتبطان بنفس المنطقة في الذاكرة ، سأقول له
;”myOtherCar .color = “Red
أنا فعلياً هنا قمت بتغيير اللون لـ myOtherCar و ليس myCar ، و سأقوم مباشرة بطباعة myCar.color
و للتوضيح باختصار : انا قمت بتعريف متغير myCar و قمت بوضع اللون الأسود له ، ثم قمت بتعريف Object آخر من النوع Car و قمت بإسناد موقع الـ myCar إلى myOtherCar ، و قمت بتغيير myOtherCar.color ،و سأقوم بطباعة الـ myCar الأولى ، عند التشغيل سيظهر Red المعدلة و ليس Black ، لأنه كما ذكرنا أنه عبارة عن مكان موجود في الذاكرة و هناك أكثر من خيط يشير له ، فأي تعديل على أحدهما يؤثر على الآخر ، فـ myCar و myOtherCar هما عبارة عن خيوط للمكان المحجوز في الذاكرة نفسه .

 

ماذا لو أردت أن أقطع هذا الخيط ، بمعنى أريد فصل myOtherCar عن المكان الذي قمت بتعريفه في الأعلى . بمعنى أن تنفصل myOtherCar عن myCar ، فأنا أريد جعلها تشير إلى مكان آخر في الذاكرة ، فعلياً في السطر 17 أنت تعرف خيطاً فقط لا يشير إلى شيء ، و لا يوجد معلومات به ، و لا نعرف كيف سيكون و ما هي محتوياته ، لذلك يوجد جملة myOtherCar = null هذه القيمة null فعلياً فارغة ، و بذلك في السطر 20 قلت له أن يقوم بفصل myOtherCar عن أي شيء آخر ، و اجعله فارغاً لا يحوي أي شيء ، و بذلك ، و بدءاً من هذا السطر لو قمت بعملية طباعة أي معلومة من معلومات  myOtherCar فسنلاحظ أنه سيظهر مشكلة و خطأ برمجي ، و هو : myOtherCar was null  أي أنه فارغ !! و لا أعرف ما هو الـ color الذي تريده ، و لذلك لا اعرف كيفية طباعة هذه الجملة ، و بالتالي يظهر الخطأ .

إذاُ .. فعليا في السطر 20 قمت بقطع الحبل الخاص بـ myOtherCar الذي كان يربطه بالبالون ؛ و عند هذا السطر ، فقط myCar هو الذي يحتوي الوصلة للبالون .

 

إذا قمت بعد ذلك بقص الخيط الثاني  و ذلك بجعل myCar  تساوي null أيضاً ، و لاحظوا أنني هنا بعد تعريف الـ  Object و اضافته أن هناك بالون معين و يحتوي على المعلومات القيمة التي أريدها . و بعد السطرين 20 و 21 قمت بقطع الشريطين ، و طار البالون في الهواء و المعلومات فعلياً ذهبت إلى الأعلى و انفجرت !!

إذاً القضية التي أريد أن اوضحها لكم أن في هذه الخطوات هي فعلياً عملية ربط Object معين بموقع ، و عندما تقوم بفصله ، أو عندما يصل إلى نهاية الـ Block الذ تم تعريفه داخله فإنه فعلياً يصبح null  أي فارغاً بغض النظر عن السطرين 20 و 21 .

 

هنا يأتي دور Dot Net Runtime فماذا يفعل ؟؟

هناك معلومات قيمة و معلومات كثيرة ، فنحن الآن نتكلم عن كلمتين أو خمس حروف و هكذا ، و لكن ماذا عن المشاريع الكبيرة ، عندنا كم هائل من الـ  Data و هذه البيانات إذا بقيت في الذاكرة و لم يتم التحكم بها ، فسيكون هناك مشاكل كبيرة نحن بغنى عنها ، و هنا يأتي دور الـ Dot Net Runtime  و الذي قلنا سابقاً أن اختصاره هو CLR سيقوم بالتالي :

ستذهب و تبحث في جميع الأكواد التي قمنا بكتابتها في هذا البرنامج ، و ستجد أن هناك بالون فارغ لا يوجد أي وصلة له ، و تقوم بالبحث إن كان هناك خيوط مرتبطة بهذا البالون أم لا ، و فعلياً لن تجد أي وصلة له ، و تقول : ” أنت يا بالون خارج  التغطية و لا أريدك ، سأقوم بتهيئة مكانك ، قم باستخدامه لمتغير آخر استفيد منه خلال البرنامج ”

إذا هذا الموضوع الذي يقوم بحذف و إدارة الذاكرة شيء يسمى Garbage Collector ، هو شيء خاص بـ CLR ، و يقوم بالبحث عن المناطق أو الـ Object  التي تم فصل الارتباط بها ، و يقوم بتهيئتها لاستخدامها بشيء آخر ، هذا المصطلح يجب أن تكونوا على اطلاع عليه ، و نحن عندما نقول أن لغة السي شارب هي لغة managed  يعني أنه يوجد شيء وسيط بينك و بين الآلة يقوم بإدارة أمور معينة أنت في غنى عنها ، و لكن لاحظوا أننا عندما نقول عن لغة السي و السي بلس بلس هي لغة unmanaged  ، فإنك عندما تقوم بالتعامل مع هذه اللغات فأنت مضطر لمتابعة المتغيرات و الذاكرة و مواضيع أخرى ، فأنت مجبر أن تقوم يدوياً بعمل تفرغ لكل Object  قمت بالانتهاء منه ، كي لا تحدث مشاكل في الذاكرة و حجم البرامج ، و مشاكل كبيرة أنت في غنىً عنها .

لماذا اخترنا السي شارب ؟؟
لأنها لغة managed ، حيث توفر لنا إمكانيات و أدوات نحن في غنىً عن الغوص في تفاصيلها ، فنحن نريد أن نركز على فكرة التطبيق الذي نريد عمله

 

الآن في السطر رقم 13 عند تعريف Object  جديد قمت باستخدام قوسي الدالة () ، الحقيقة هنا يتم استدعاء  Method داخل الكلاس حتى و إن لم تقم بكتابتها و تعريفها ، ألا و هي دالة الـ Constructer  ، و هي دالة يتم استدعاؤها عندما تقوم بعمل initialize  للـ Object ، بمعنى أنه عندما تقوم بعمل فتح و اغلاق قوس يتم تنفيذ شيء معين ، و فعلياً هنا لأننا لم نقم بكتابة شيء ، هو قام بمناداته ، و لكنه ضمنياً فارغ .

لكي نتعامل مع هذا الموضوع و نتعرف عليه أكثر ، سنقوم الآن بالتعرف على كيفية كتابة الـ constructer  .

شكلها كالتالي :

()public Car

{
}

و اسمها طبق الأصل مع اسم الكلاس ..
هذه الأسطر من 29 إلى 32 ضمنياً موجودة ، و لكنها فارغة ، طالما قمت بكتابتها ، فالأولوية لما قمت بكتابته ، سأقوم بعمل التالي :

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

أنا فعلياً قمت بتعريف الـ Object ، و لكن لم أقم بإسناد أي قيمة ، فقط قم بطباعة الـ Make  و عندما أقوم بتنفيذ التطبيق سيقوم بطباعة كلمة Nissan  ؛ من أين أتت هذه الكلمة !!!!

هذه الكلمة أتت من الـ Constructer    و هو كما قلت يتم استدعاؤه ضمنياً عندما تقوم بعملية استنساخ Object  جديد ،

هنا أيضاً مفهوم آخر ذكرناه في الMethods و سنعيده مرة أخرى  ، ألا و هو الـ Overload ، و هو إمكانية كتابة نفس الـ Method بنفس الاسم ، لكن مع اختلاف عدد الـ Parameter الموجود بداخلها ، لاحظوا أننا تكلمنا أنه داخل الـ Method بإمكانك إضافة  Parameter معينة  يتم اسنادها من خارج الدالة و يتم التعامل معها من داخل الدالة .

سنقوم بإضافة دالة جديدة و هي عبارة عن public Car و لكن سنقوم بإضافة Parameter  من خارج الدالة ، و تسمى هذه العملية Overload ، فسأقول له مثلاً string make  و string model  و int year و string color  ، و لاحظوا أن الكلمة make  تختلف تماما عن الكلمة Make لأننا تكلمنا أن لغة سي شارب هي لغة Case sensitive  فالحرف الصغير يختلف عن الكبير ، إذا في الـ overload هذه هنا سأقوم بعمل التالي ، حيث أنني في السطر رقم 13 ثمت بتعريف Object  جديد و القوسين فارغين ، فعليا قام باستدعاء الدالة التي قمنا بكتابتها ، و لكن أريد منه أن يقوم في عملية الـ Initialization أن أسند إليه قيماً و يقوم مباشرة بوضعها داخل كيان الـ Object  للتعامل معها لاحقاً .

فمثلاً سأقول له كالتالي :

الـ Make ذات الحرف الكبير اجعلها الـ make ذات الحرف الصغير ،  و التي أرسلتها من خارج الدالة

و كذلك بالنسبة للـ Model & Year & Color ، نعود للأعلى في السطر 13 ، بمجرد فتح القوس الأول يقوم بإظهار خيارات ، و بالضغط على الأسهم الظاهرة ، ستلاحظ أنه يوجد أكثر من Method يمكن استدعاؤها ، و هنا الأولى لا تحتاج أي Parameter و التي استخدمناها في السطر السابق ، و لكن هنا أريد استخدام الثانية ، حيث أريد اسناد الشركة و الموديل و السنة و اللون في نفس السطر ، فسأقول له  BMW مثلا , و كذلك الموديل و السنة و اللون ، إذا قمت بإنشاء Object جديد و قمت باستدعاء دالة البناء التي تحتوي على Parameter .

إذا عندما أقوم بعمل طباعة لنوع الشركة ، لن يقوم بإعادة اسناد Nissan بل سيقوم بطباعة BMW لأني قمت بإسنادها في بداية انشاء التطبيق  .

فهذا هو مفهوم الـ Constructer ، و هو مميز جداً   ، و استخدامه مفيد جداً في حالات كثيرة .

 

كلمة Static :

القسم الرابع من درسنا هو كلمة Static  ، لاحظوا أننا من أول تطبيق و حتى الآن ، كانت ترافقنا كلمة static الموجودة في الـ voidMain ، فقد تكلمنا عن int و string و void و تحدثنا عن الـ Class و كيف يمكننا استخدامه  و تحدثنا عن كلمةnew و تعلمنا كيف يمكن استخدامها  .

لطن ما معنى كلمة static ، و لماذا ترافقنا دائماً ، و لماذا في درس الدوال عندما قمنا بإضافة دالة ، وضعناها أيضاً من النوع Static كي نتمكن من استخدامها داخل الـ  voidMain ؟

الحقيقة أن كلمة static هي شيء يكون standard في كل الكلاس ، فكلمة Make و Model و Year و Color هي لا تتبع للكلاس ،هذه تتبع للـ Object المستنسخ من الكلاس ، فإن قمت بكتابة Car و بعدها نقطة ، فلن أجد الـ property السابقة ، لماذا ؟؟

لأن الـ Property الموجودة هنا تكون تابعة للـ Object الذي يأتي بعد عملية الاستنساخ ، فهنا عندما قمت باستنساخ Object جديد و وضعت myCar و النقطة ، حصلت على كل الـ Property الموجودة فيه ، و لدي أيضاً دوال ستكون متاحة هنا ، لماذا؟

لأنها ليست من النوع static

أما عندما أقوم بتعريف متغير جديد من النوع static ، لاحظوا أنني قمت بتعريف Property جديدة و لكنها من النوع static ، فهي تعني أن هذه الـ Property يمكن استدعاؤها من الكلاس نفسه ، بذكر اسم الكلاس قم ارفاقه مباشرة و ارفاق اسم الـ static ، إذاً عندما أكتب Car و هي اسم الكلاس بالحرف الكبير ، و اضع النقطة ، مباشرة أصبح لدي Property يمكن استخدامها على مستوى الـ Class .

 

دعونا الآن نتعامل مع الكلمة static بشكل أكبر .

لاحظوا هنا في داخل الكلاس ، وضعت أنه عند تعريف متغير جديد سيقوم بتغيير الـ Make ، مبدئياً سأقوم بعملها جملة ملاحظة ، و سأضع ، عندما يتم انشاء Object جديد ، قم بزيادة الـ Count برقم 1 ، فإذا قمت بطباعة Car.Count ، فعملية الزيادة لا تتم في الـ Constructer الذي يقوم بإعطاء parameter ، فأنا عندما قمت بإنشاء مجسم جديد ، لم أقم باستخدام هذا الـ Constructer ، بل قمت باستخدام الثاني ، و الذي يقوم بإعطاء وParameter ، ففعلياً هنا لا يوجد زيادة ، فبالتالي تظهر القيمة 0 ، لذلك سنقوم بعمل جملة الزيادة جملة ملاحظة و وضعها في الـ  Constructer الثاني ، و نجعل السيارة الثانية فارغة تستدعي الـ Constructer الأول ، و بذلك تكون السيارة الأولى و الثالثة تستدعي الـ Constructer الثاني ، أما الثالثة ، فتقوم باستدعاء الأول ، و عندها في كل Constructer يتم زيادة القيمة بواحد ، و لدي 3 سيارات ، فعندما أقوم بطباعة الـ Count يجب أن يظهر الرقم 3 .

 

موضوع الـ static  ليس حصرياً على الـ Property بل يمكن استخدامه كذلك في الـ Method .

لاحظواً أننا طول الوقت ، و من أول سطر لم نقم باستنساخ مجسم من الـ Console Class ، لم نقل له أعطني مجسم جديد و اريد استخدام هذه الدالة .

لاحظوا أن الـ Method ذات الاسم WriteLine تابعة مباشرة لاسم الكلاس ، و ليست تابعة لـ Object يتم اشتقاقه من الكلاس .

ايضاً مر معنا في درس التاريخ و الوقت أننا قمنا بالتعامل مع هذه الـ Property المسماة Now ، و هي لا تحتاج لعملي اشتقاق من الـ Data Type المسماة DateTime ، فقد حصلت عليها مباشرة من خلال النقطة ، لأن هذه الProperty عبارة عن static .

أيضاً لو لاحظتم في درس المصفوفات ، حيث تعاملنا مع دالة Reverse ، و هي ايضاً لا تحتاج لعملية اشتقاق من الكلاس Array .

إذا بالمثل سأقوم بإنشاء Method جديدة ، هذه الدالة من النوع static  أي أنها تابعة للكلاس بشكل كامل ، و سأقوم باستدعائها بالكلاس الرئيسي ، و حيث أن هذه الدالة تقوم بطباعة Count
و لو قمت بتنفيذ التطبيق سيقوم بطباعة عدد السيارات التي قمت بإنشائها من هذا الكلاس .

 

مصطلحات الدرس :

Garbage Collector :

Static :

Constructer :

Object Oriented Programming (OOP) :

Property   :

Object :

Class :

Dot Net Runtime :

Common Language Runtime (CLR) :

namespace   :

Nested Class :

null   :

managed   :

unmanaged  :

initialize   :

Initialization :

Parameter :

Reverse :

Count :

Public  :

الأسطر البرمجية التي قمنا بتنفيذها في هذا الدرس

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ObjectLifeTime
{
    class Program
    {
        static void Main(string[] args)
        {
            //Car myCar = new Car();
            Car myCar = new Car("BMW","Z",1990,"Yellow");
            Car myCar2 = new Car();
            Car myCar3 = new Car("BMW","Z",1990,"Yellow");
            
            Car.PrintCount();
            //myCar.Color = "Black";

            //Car myOtherCar;
            //myOtherCar = myCar;
            //myOtherCar.Color = "Red";
            //myOtherCar = null;
            //myCar = null;
            
            //Console.WriteLine("{0}", Car.Count);
            Console.ReadLine();
            
        }
    }
    class Car
    {
        public Car()
        {
            //Make = "Nissan";
            Count++;
        }
        public Car(string make,string model,int year,string color)
        {
            Make = make;
            Model = model;
            Year = year;
            Color = color;
            Count++;
        }

        public static void PrintCount()
        {
            Console.WriteLine("{0}", Count);
        }

        public static int Count { get; set; }
        public string Make { get; set; }
        public string Model { get; set; }
        public int Year { get; set; }
        public string Color { get; set; }
    }
}

2 thoughts on “#19 مواضيع متقدمة عن Classes – البرمجة بواسطة #C

  1. السلام عليك أستاذ ديب
    أريد الاستفسار عما إذا سلسلة تعلم البرمجة للمبتدئين قد انتهت عند الدرس 19 ؟
    وأيضا هل سوف تبدأ في تعليم تطوير الألعاب؟ ومتى سوف يكون ذلك ؟
    وإذا سمحت أستاذي أريدك أن تنصحني بكيف أستطيع تطبيق ماتعلمته حتى الآن من السلسلة حتى أطور مهاراتي
    شكرا لك

    • وعليكم السلام ورحمة الله وبركاته، السلسلة لم تنتهي بعد، هناك حوالي 3-5 دروس متبقية وبعدها ننتقل إلى دورة تطوير الألعاب يونيتي. حاولي عمل تطبيق إستئجار السيارات ثم قومي بتطبيقه بشيء آخر.

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *