المحتوى عن 'دوت نت'.



مزيد من الخيارات

  • ابحث بالكلمات المفتاحية

    أضف وسومًا وافصل بينها بفواصل ","
  • ابحث باسم الكاتب

نوع المُحتوى


التصنيفات

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

التصنيفات

  • PHP
    • Laravel
    • ووردبريس
  • جافاسكريبت
    • Node.js
    • jQuery
    • AngularJS
    • Cordova
  • HTML5
  • CSS
    • Sass
    • إطار عمل Bootstrap
  • SQL
  • سي شارب #C
    • منصة Xamarin
  • بايثون
    • Flask
    • Django
  • لغة روبي
    • إطار العمل Ruby on Rails
  • لغة Go
  • لغة جافا
  • لغة Kotlin
  • برمجة أندرويد
  • لغة Swift
  • لغة R
  • لغة TypeScript
  • سير العمل
    • Git
  • صناعة الألعاب
    • Unity3D
  • مقالات برمجة عامة

التصنيفات

  • تجربة المستخدم
  • الرسوميات
    • إنكسكيب
    • أدوبي إليستريتور
    • كوريل درو
  • التصميم الجرافيكي
    • أدوبي فوتوشوب
    • أدوبي إن ديزاين
    • جيمب
  • التصميم ثلاثي الأبعاد
    • 3Ds Max
    • Blender
  • مقالات تصميم عامة

التصنيفات

  • خواديم
    • الويب HTTP
    • قواعد البيانات
    • البريد الإلكتروني
    • DNS
    • Samba
  • الحوسبة السّحابية
    • Docker
  • إدارة الإعدادات والنّشر
    • Chef
    • Puppet
    • Ansible
  • لينكس
  • FreeBSD
  • حماية
    • الجدران النارية
    • VPN
    • SSH
  • مقالات DevOps عامة

التصنيفات

  • التسويق بالأداء
    • أدوات تحليل الزوار
  • تهيئة محركات البحث SEO
  • الشبكات الاجتماعية
  • التسويق بالبريد الالكتروني
  • التسويق الضمني
  • استسراع النمو
  • المبيعات

التصنيفات

  • إدارة مالية
  • الإنتاجية
  • تجارب
  • مشاريع جانبية
  • التعامل مع العملاء
  • الحفاظ على الصحة
  • التسويق الذاتي
  • مقالات عمل حر عامة

التصنيفات

  • الإنتاجية وسير العمل
    • مايكروسوفت أوفيس
    • ليبر أوفيس
    • جوجل درايف
    • شيربوينت
    • Evernote
    • Trello
  • تطبيقات الويب
    • ووردبريس
    • ماجنتو
  • أندرويد
  • iOS
  • macOS
  • ويندوز

التصنيفات

  • شهادات سيسكو
    • CCNA
  • شهادات مايكروسوفت
  • شهادات Amazon Web Services
  • شهادات ريدهات
    • RHCSA
  • شهادات CompTIA
  • مقالات عامة

أسئلة وأجوبة

  • الأقسام
    • أسئلة ريادة الأعمال
    • أسئلة العمل الحر
    • أسئلة التسويق والمبيعات
    • أسئلة البرمجة
    • أسئلة التصميم
    • أسئلة DevOps
    • أسئلة البرامج والتطبيقات
    • أسئلة الشهادات المتخصصة

التصنيفات

  • ريادة الأعمال
  • العمل الحر
  • التسويق والمبيعات
  • البرمجة
  • التصميم
  • DevOps

تمّ العثور على 6 نتائج

  1. سنتحدّث في هذا الدرس عن كيفيّة تطبيق مبادئ البرمجة كائنيّة التوجّه في سي شارب وذلك من خلال إنشاء واستخدام الأصناف والكائنات في هذه اللغة. يمكن التصريح عن صنف في سي شارب باستخدام الكلمة المحجوزة class يليها اسم الصنف وهو يتبع لنفس قواعد التسمية للمتغيّرات، علمًا أنّه يفضّل أن يكون الحرف الأوّل من اسم الصنف حرفًا طباعيًّا كبيرًا. انظر إلى الشكل التالي حيث نرى الصنف البسيط Employee والذي يُعبّر عن موظّف في إحدى الشركات: يحتوي هذا الصنف على ثلاثة حقول بيانات data fields هي: الاسم FirstName الكنية LastName الراتب Salary تستطيع اعتبارها حاليًّا أنّها تمثّل خصائص للصنف Employee، كما يحتوي هذا الصنف على تابع وحيد اسمه DisplayInfo الهدف منه هو الحصول على تمثيل نصيّ لكلّ كائن ننشئه من هذا الصنف كما سنرى بعد قليل، يشبه التابع إلى حدٍّ كبير الدّالة function في لغات البرمجة الأخرى. لا يتطلّب هذا التابع أيّ وسائط في حين أنّه يُرجع قيمة نصيّة من النوع string. هذه الحقول بالإضافة إلى التابع السابق تُعتبر أعضاء ضمن الصنف Employee كما ذكرنا ذلك مسبقًا. تقع أعضاء أيّ صنف ضمن حاضنتيه. لاحظ الكلمة المحجوزة public والموجودة قبل كلّ تصريح لحقل أو تابع ضمن الصنف Employee. هذه الكلمة عبارة عن مُحدّد وصول access modifier. تتحكّم محدّدات الوصول بقابلية الوصول إلى أعضاء الصنف من خارجه، سنتعامل مع نوعين آخرين من محدّدات الوصول وهما private و protected. يكفي أن تعلم الآن أنّ أي عضو في الصنف يمتلك محدّد وصول public يمكن الوصول إليه سواءً من داخل الصنف (أو بشكل أدق من داخل الكائن) أو من خارجه. كما من المفيد أن نعلم أنّه من الممكن استخدام محدّدات الوصول مع الأصناف أيضًا كما سنرى في درس لاحق. إذا أردنا إنشاء كائن جديد من الصنف Employee فعلينا التصريح عن متغيّر مناسب من النوع Employee وذلك على الشكل التالي: Employee empObject; صرّحنا عن المتغيّر empObject على أنّه من النوع Employee. لاحظ التشابه في التصريح عن المتغيّرات بين أنواع موجودة ضمن سي شارب وبين أنواع ننشئها بأنفسنا. التصريح السابق غير كافي لإنشاء الكائن. لإنشاء كائن من النوع Employee علينا استخدام العامل new الذي يعمل على إنشاء كائن من أيّ صنف نرغبه ويعمل على إعادة المرجع (العنوان) لذلك الكائن في الذاكرة. استخدام العامل new سهل حيث يمكننا كتابة ما يلي بعد عبارة التصريح السابقة: empObject = new Employee(); يقوم العامل new بإنشاء كائن جديد من الصنف Employee ثمّ يُسند مرجع (عنوان) هذا الكائن ضمن المتغيّر empObject. لاحظ القوسين الموجودين بعد اسم الصنف Employee. في الحقيقة يُعبّر هذين القوسين عن استدعاء لبانية constructor الصنف Employee عند إنشاء الكائن. ولكن أين هذه البانية؟ هذا ما سنراه بعد قليل. يمكن الآن الوصول إلى الحقول والتوابع الموجودة ضمن الكائن عن طريق كتابة المتغيّر الذي يحوي العنوان إلى الكائن (أي المتغيّر empObject) ثم نضع نقطة وبعدها اسم الحقل أو التابع الذي نريد الوصول إليه. في العبارة التالية سنسند القيمة "Mohammad" إلى الحقل FirstName من الكائن empObject (الكائن الذي يشير إليه empObject): empObject.FirstName = "Mohammad"; حان الآن وقت التنفيذ العمليّ. انظر إلى البرنامج Lesson06_01 الذي يوضّح كيفية إنشاء الصنف Employee وكيفيّة إنشاء كائنين منه: 1 using System; 2 3 namespace Lesson06_01 4 { 5 6 class Employee 7 { 8 public string FirstName; 9 public string LastName; 10 public double Salary; 11 12 public string DisplayInfo() 13 { 14 string result = string.Format("{0} {1} - Salary: {2:N0}", 15 this.FirstName, this.LastName, this.Salary); 16 17 return result; 18 } 19 } 20 21 class Program 22 { 23 static void Main(string[] args) 24 { 25 Employee employee1, employee2; 26 27 employee1 = new Employee(); 28 employee1.FirstName = "Mohammad"; 29 employee1.LastName = "Mansoor"; 30 employee1.Salary = 1000; 31 32 employee2 = new Employee(); 33 employee2.FirstName = "Saleh"; 34 employee2.LastName = "Mahmoud"; 35 employee2.Salary = 2500; 36 37 Console.WriteLine("First Employee: {0}", employee1.DisplayInfo()); 38 Console.WriteLine("Second Employee: {0}", employee2.DisplayInfo()); 39 } 40 } 41 } عند تنفيذ البرنامج سنحصل على الخرج التالي: First Employee: Mohammad Mansoor - Salary: 1,000.00 Second Employee: Saleh Mahmoud - Salary: 2,500.00 نلاحظ من النظرة الأولى للبرنامج السابق أنّه لدينا صنفان ضمن نطاق الاسم Lesson06_01 وهما Employee و Program. يقع التصريح عن الصنف Employee في الأسطر بين 6 و 19 ويحتوي هذا الصنف كما رأينا قبل قليل على أربعة أعضاء وهي عبارة عن ثلاثة حقول FirstName و LastName و Salary بالإضافة إلى التابع DisplayInfo الموجود بين السطرين 12 و18. تنحصر وظيفة هذا التابع في الحصول على التمثيل النصيّ لأيّ كائن ننشئه من الصنف Employee. يحتوي التابع DisplayInfo على أسلوب جميل لتنسيق النصوص يشبه ذلك الأسلوب الذي كنّا نستخدمه مع التابع WriteLine. يحتوي الصنف string على تابع اسمه Format يقبل عدّة وسائط (السطر 14) أولها نصّ تنسيقي، أمّا الوسائط التالية فهي القيم التي ستجد لها أمكنةً ضمن النص التنسيقي، كما كنّا نستخدم التابع WriteLine بالضبط. يُرجع التابع Format نصًّا منسّقًا بحسب القيم الممرّرة له. الشيء الوحيد المختلف هو كيفيّة تنسيق قيمة الراتب Salary باستخدام مُحدّد التنسيق :N0 الموجود ضمن {2:N0}. يخبر هذا المحدّد التابع Format أنّ القيمة التي ستوضع في هذا المكان (وهي قيمة Salary) يجب أن تُنسّق على شكل رقم ذي فاصلة آلاف وبدون فاصلة عشريّة. يفيد مثل هذا التنسيق في الحصول على أرقام منسّقة بشكل محترف تُعبّر عن الراتب الذي يحصل عليه الموظّف وهي تبدو مثل 1,000 أو 2,500. جرّب استخدام التنسيق {2:N1} و {2:N2} ولاحظ الفرق. لاحظ أنّني قد استخدمت الكلمة المحجوزة this متبوعةً بنقطة قبل اسم كل حقل. في الحقيقة تُشير هذه الكلمة إلى الكائن الحالي الذي يتمّ منه استدعاء التابع DisplayInfo كما سنرى ذلك بعد قليل. أمّا لإرجاع القيمة النصيّة من التابع DisplayInfo فإنّنا ببساطة نستخدم الكلمة المحجوزة return ونضع بعدها القيمة المراد إرجاعها. الصنف Program المصرّح عنه في الأسطر بين 21 و 40 هو الصنف الذي تعاملنا معه في جميع البرامج التي كتبناها حتى الآن. يحتوي هذا الصنف على التابع Main الذي يمثّل نقطة الدخول للبرنامج كما نعلم. يبدأ التابع Main بالتصريح عن متغيرين من النوع Employee وهما employee1 و employee2 ثمّ ينشئ كائنًا من النوع Employee باستخدام العامل new (السطر 27) ويسنده إلى المتغيّر employee1. بعد ذلك يمكن استخدام أيّ حقل أو تابع معرّف ضمن الصنف Employee عن طريق المتغيّر employee1 بشرط أن يكون له محدّد وصول public كما هو واضح في الأسطر من 28 حتى 30. يتكرّر نفس الأمر بالنسبة للمتغيّر employee2 الذي سيحمل كائنًا مختلفًا عن الكائن الموجود ضمن employee1. أخيرًا وفي السطرين 37 و38 يتم طباعة التمثيل النصيّ لكلّ من الكائنين باستخدام التابع DisplayInfo. تجدر الإشارة إلى أنّه عند وصول تنفيذ البرنامج إلى السطر 37 وإلى الاستدعاء ()employee1.DisplayInfo تحديدًا سيؤدّي ذلك إلى انتقال التنفيذ إلى السطر 14 ضمن هذا التابع لتنفيذ التعليمات البرمجيّة ضمنه ومن ثمّ الحصول على التمثيل النصيّ للكائن employee1 وإرجاعه إلى السطر 37 مرّة أخرى ليعمل البرنامج على تمرير هذه القيمة النصيّة للتابع WriteLine ومن ثمّ العرض على الشاشة، وبالطبع يتكرّر نفس الأمر تمامًا بالنسبة للكائن ضمن employee2 في السطر 38. إذا كنت تستخدم Visual Studio 2015 بأيّ إصدار فأنصحك أن تنفّذ هذا البرنامج بشكل خُطَوي لكي تتعرّف على آلية عمل هذا البرنامج بشمل عمليّ. اضغط على المفتاح F11 (أو من القائمة Debug > Step Into) لتنفيذ البرنامج باستخدام منقّح الأخطاء debugger. ستلاحظ ظهور مستطيل أصفر يُشير إلى مكان التنفيذ الحالي، وكلما ضغطت المفتاح F11 سينتقل تنفيذ البرنامج إلى العبارة البرمجيّة التالية خطوة بخطوة. البانية constructor ضمن الصنف البانية constructor هي تابع من نوع خاص يجب أن تكون موجودة ضمن أيّ صنف في سي شارب. في حال تمّ إغفالها سيعمل المترجم على توليد واحدة افتراضيّة من أجلنا. في الحقيقة وظيفة البانية هي بناء الكائن وحجز مكان مناسب له في الذاكرة، حيث يتم استدعاء البانية عند إنشاء الكائن باستخدام العامل new. لا يمكن للبواني إرجاع قيمة مخصّصة كما نفعل مع التوابع الأخرى عادةً، في الحقيقة هي تُرجع كائنًا من الصنف الموجودة ضمنه. ولكن يمكن أن تقبل وسائط نمرّرها إليها. استبدل الصنف Employee التالي بذلك الموجود ضمن البرنامج Lesson06_01: 1 class Employee 2 { 3 public string FirstName; 4 public string LastName; 5 public double Salary; 6 7 public Employee() 8 { 9 Console.WriteLine("Hello, I'm in Employee's constructor!"); 10 } 11 12 public string DisplayInfo() 13 { 14 string result = string.Format("{0} {1} - Salary: {2:N0}", 15 this.FirstName, this.LastName, this.Salary); 16 17 return result; 18 } 19 } لقد أضفنا في هذه النسخة البانية ()Employee للصنف Employee. نفّذ البرنامج لتحصل على الخرج التالي: *** Hello, I'm in Employee's constructor! *** *** Hello, I'm in Employee's constructor! *** First Employee: Mohammad Mansoor - Salary: 1,000 Second Employee: Saleh Mahmoud - Salary: 2,500 لاحظ أنّ العبارة: *** Hello, I'm in Employee's constructor! *** قد ظهرت مرّتين في الخرج، وذلك بسبب أنّنا أنشأنا كائنين حيث تُنفّذ هذه البانية من أجل كلّ عملية إنشاء. ولكن السؤال المطروح هنا، ماذا سنستفيد من هذه البانية؟ تُستخدم البواني عمومًا عندما نريد تهيئة الكائن ببعض القيم الضرورية لجعل حالته مستقرّة وذلك أثناء إنشائه وقبل محاولة الوصول إليه من أيّ مصدر خارجيّ. انظر الآن إلى الصنف Employee المعدّل الذي يحوي بانية تقوم ببعض الأعمال المفيدة: 1 class Employee 2 { 3 public string FirstName; 4 public string LastName; 5 public double Salary; 6 7 public Employee(string firstName, string lastName, double salary) 8 { 9 this.FirstName = firstName; 10 this.LastName = lastName; 11 this.Salary = salary; 12 } 13 14 public string DisplayInfo() 15 { 16 string result = string.Format("{0} {1} - Salary: {2:N0}", 17 this.FirstName, this.LastName, this.Salary); 18 19 return result; 20 } 21 22 } تتطلّب البانية هذه المرّة ثلاثة وسائط، تمثّل قيمًا سيتمّ إسنادها إلى الحقول. هذه الوسائط هي: firstName و lastName و salary (لاحظ أنّ اسم كلّ منها يبدأ بحرف طباعي صغير لتمييزها عن حقول الصنف). إذا استبدلت هذا الصنف الجديد بالصنف القديم الموجود ضمن البرنامج Lesson06_01 وحاولت تنفيذ البرنامج فستحصل على خطأ. السبب في ذلك بسيط، وهو أنّ العبارتين في السطرين 27 و 32 من البرنامج Lesson06_01 تحاولان إنشاء كائنين من الصنف Employee عن طريق بانية لا تتطلّب أيّة وسائط وهذا ما لا يتوفّر في الصنف Employee الجديد. فعندما يلاحظ مترجم سي شارب وجود بانية واحدة على الأقل بصرف النظر عن عدد الوسائط التي تتطلّبها فإنّه يمتنع عن توليد بانية افتراضية بشكل تلقائي مثلما كان يفعل من قبل. يوجد حلّ سريع لهذه المشكلة يتمثّل في توفير بانية لا تحتاج لأيّة وسائط كما كان الوضع السابق. انظر إلى النسخة الأخيرة للصنف Employee: 1 class Employee 2 { 3 public string FirstName; 4 public string LastName; 5 public double Salary; 6 7 public Employee(string firstName, string lastName, double salary) 8 { 9 this.FirstName = firstName; 10 this.LastName = lastName; 11 this.Salary = salary; 12 } 13 14 public Employee() 15 { 16 17 } 18 public string DisplayInfo() 19 { 20 string result = string.Format("{0} {1} - Salary: {2:N0}", 21 this.FirstName, this.LastName, this.Salary); 22 23 return result; 24 } 25 26 } بعد اعتماد هذا الصنف ضمن البرنامج Lesson06_01، سيعمل البرنامج الآن بشكل طبيعي ويظهر الخرج كما هو متوقّع. ولكن تأمّل معي هذا الصنف قليلًا، ألا تلاحظ وجود بانيتين له؟ هذا أمر طبيعي ووارد جدًّا في سي شارب حيث يمكن كتابة أكثر من تابع بنفس الاسم طالما اختلف عدد أو أنواع الوسائط الممرّرة لكلّ منهما. نسمي هذه الميزة بزيادة التحميل overloading للتوابع. فعند وجود استدعاء للتابع المزاد تحميله يتمّ اختيار الشكل المناسب بناءً على عدد وأنواع الوسائط الممرّرة. لاحظ أنّ البانية عديمة الوسائط فارغة ولا بأس في ذلك. ولكنّ السؤال هنا كيف يمكن الاستفادة من البانية ذات الوسائط الثلاثة. الأمر بسيط، استبدل محتويات التابع Main في البرنامج Lesson06_01 بالشيفرة البسيطة المكافئة التالية: 1 Employee employee1, employee2; 2 3 employee1 = new Employee("Mohammad", "Mansoor", 1000); 4 employee2 = new Employee("Saleh", "Mahmoud", 2500); 5 6 Console.WriteLine("First Employee: {0}", employee1.DisplayInfo()); 7 Console.WriteLine("Second Employee: {0}", employee2.DisplayInfo()); انظر كم أصبحت الشيفرة نظيفة وقصيرة ومريحة للعين. إليك الآن البرنامج Lesson06_02 كاملًا بعد التعديل: 1 using System; 2 3 namespace Lesson06_02 4 { 5 6 class Employee 7 { 8 public string FirstName; 9 public string LastName; 10 public double Salary; 11 12 public Employee(string firstName, string lastName, double salary) 13 { 14 this.FirstName = firstName; 15 this.LastName = lastName; 16 this.Salary = salary; 17 } 18 19 public Employee() 20 { 21 22 } 23 24 public string DisplayInfo() 25 { 26 string result = string.Format("{0} {1} - Salary: {2:N0}", 27 this.FirstName, this.LastName, this.Salary); 28 29 return result; 30 } 31 32 33 } 34 35 36 class Program 37 { 38 static void Main(string[] args) 39 { 40 Employee employee1, employee2; 41 42 employee1 = new Employee("Mohammad", "Mansoor", 1000); 43 employee2 = new Employee("Saleh", "Mahmoud", 2500); 44 45 Console.WriteLine("First Employee: {0}", employee1.DisplayInfo()); 46 Console.WriteLine("Second Employee: {0}", employee2.DisplayInfo()); 47 } 48 } 49 } تمارين داعمة تمرين 1 أضف تابعًا جديدًا إلى الصنف Employee الموجود في البرنامج Lesson06_02 السابق وسمّه GetSalaryAfterTax. وظيفة هذا التابع هي الحصول على قيمة الراتب للموظّف بعد تطبيق الضريبة Tax عليه. اعتبر نسبة الضريبة 2%. تلميح: اضرب قيمة الراتب Salary بالعدد 0.98 للحصول على قيمة الراتب بعد خصم الضريبة. فإذا كان الراتب 1500 مثلًا، يجب أن يُرجع التابع GetSalaryAfterTax القيمة 1470. تمرين 2 أنشئ صنفًا جديدًا سمّه MyRectangle والذي يُعبّر عن مستطيل في المستوي، بحيث يحتوي على الحقلين Width و Height (من النوع double لكلّ منهما)، بالإضافة إلى التابع GetArea لحساب مساحة المستطيل. ثمّ اكتب برنامجًا بسيطًا يوضّح استخدام هذا الصنف من خلال إنشاء كائنين منه. احسب مساحة كل مستطيل (كائن) واعرض النتيجة على الشاشة. الخلاصة تعلّمنا في هذا الدرس أساسيّات إنشاء الأصناف والكائنات، وكيفية التعامل مع الحقول والتوابع والبواني الموجودة ضمن الصنف. كما أخذنا لمحة سريعة حول محدّدات الوصول وكيفية التعامل مع محدّد الوصول public، علمًا أنّنا ستوضّح كيفيّة التعامل مع باقي المحدّدات في الدرس التالي الذي سنتحدّث فيه عن المزيد حول هذا الموضوع المهم والأساسي لتطوير التطبيقات باستخدام سي شارب.
  2. يُعتبر إطار العمل دوت نت NET Framework. من شركة مايكروسوفت من أُطر العمل المشهورة جدًّا، فمنذ الإصدار التجريبي الأوّل أواخر عام 2000 وحتى الإصدار 4.6 حاليًّا شهد تحسينات كبيرة جعلت من لغات البرمجة التي تعمل بالاعتماد عليه لغات برمجة غنيّة ومعاصرة. يُعتبر إطار العمل دوت نت الكيان الأساسيّ التي تعتمد عليه التطبيقات في تنفيذ المهام المطلوبة منها. فهو يوفّر الوسائل اللازمة لوصول مثل هذه التطبيقات إلى الملفات والتعامل مع HTTP والوصول والتعامل مع قواعد البيانات وغيرها من المهام الأساسيّة التي قد يحتاجها أيّ تطبيق حاسوبيّ. صُمّم إطار العمل دوت نت منذ البداية لكي يعمل على أيّ نظام تشغيل أو أيّ عتاد صلب متاح، وعلى الرغم من أنّ مايكروسوفت لم تدعم تشغيل إطار العمل هذا سوى على أنظمة تشغيل ويندوز، إلّا أنّه جرت محاولات مستقلة لنقله إلى لينكس عن طريق مشروع mono مفتوح المصدر. وقد نجحت هذه المحاولة بصورة لا بأس بها واستمر المشروع لعدّة سنوات، ثمّ توّج بنقله إلى أنظمة تشغيل الأجهزة الذكيّة وهذا ما قامت به شركة Xamarin التي استحوذت عليها شركة مايكروسوفت قبل أقل من شهر من الآن، حيث تمكّنت Xamarin من ذلك بالاعتماد على مشروع mono مفتوح المصدر. تُعتبر لغة سي شارب #C لغة البرمجة الأساسيّة ضمن إطار العمل دوت نت، وقد واكبْتُ تطوّرها منذ الإصدار الأوّل وحتى اليوم. ومما لا شكّ فيه أنّها قد شهدت تحسينات كبيرة ومزايا مفيدة تجمع بين القوّة والمرونة لتطوير طيف واسع من التطبيقات البرمجيّة تشمل تطبيقات سطح المكتب Desktop Applications وتطبيقات ويب باستخدام ASP.NET وتطبيقات خدمات ويب مثل WCF (اختصار لـ Windows Communication Foundation) وحتى تطبيقات الأجهزة الذكيّة التي تعمل على نظام تشغيل Windows Phone أو التي تعمل على باقي الأنظمة مثل Android و iOS من خلال منصّة Xamarin. لن ندخل في التفاصيل الدقيقة لمعمارية إطار العمل دوت نت، وذلك لجعل هذه السلسلة مبسّطة قدر المستطاع. يكفيك أن تعلم أنّ إطار العمل هذا يتكوّن من قسمين رئيسيّين: بيئة التنفيذ المشتركة Common Language Runtime وتُدعى اختصارًا CLR. مكتبة أصناف إطار العمل Framework Class Library وتُدعى اختصارًا FCL. حول هذه السلسلة سنتعلّم في هذه السلسلة كتابة تطبيقات باستخدام لغة سي شارب #C بأسلوب مبسّط وسلس وذلك من خلال تطبيقات موجّه الأوامر Console Applications، وسبب تفضيلي لهذه التطبيقات عن سواها، هو أنّه تسمح لك بالتركيز على تعلّم اللغة نفسها دون تشتيت الانتباه إلى العديد من الجوانب التي تتطلّبها أنواع التطبيقات الأخرى كتطبيقات سطح المكتب لويندوز على سبيل المثال، رغم أنّنا سنتحدّث القليل عنها في الدرس الأخير. ما الذي أحتاجه لكي أستفيد من هذه السلسلة؟ تفترض هذه السلسلة أنّك تمتلك معرفة عامّة بأساسيّات البرمجة. ستحتاج أيضًا إلى حاسوب ذو نظام تشغيل ويندوز 7.1 أو أعلى. بالإضافة إلى تحميل وتنصيب بيئة التطوير Visual Studio 2015 Community المجّانيّة. كما يمكنك تجريب معظم البرامج الموجودة في هذه السلسلة حتى ولو لم يكن لديك نظام تشغيل ويندوز أصلًا عن طريق موقع NET Fiddle. ولكن لن تستفيد بهذه الطريقة من المزايا الهامّة التي يوفّرها لك Visual Studio. الأقسام الأساسية لإطار العمل دوت نت سنتحدّث قليلًا عن القسمين الأساسيّين لإطار العمل دوت نت بالإضافة إلى مترجم سي شارب C# Compiler، لكي تتكوّن لدينا الصورة الواضحة لآليّة عمل تطبيقات دوت نت. بيئة التنفيذ المشتركة CLR هي البيئة التي تنفّذ البرامج المكتوبة بلغات دوت نت، وتشكّل حاضنةً لهذه البرامج من خلال عزلها عن العتاد الصلب hardware للجهاز التي تعمل عليه. تستطيع تشبيهها بالآلة الافتراضيّة Virtual Machine الخاصّة بلغة Java. وهي مسؤولة عن إدارة المصادر التي يحتاجها البرنامج في عمله، وفي مقدّمتها الذاكرة. حيث تعمل هذه البيئة على إدارة عمليّات إنشاء الكائنات التي يحتاجها البرنامج، ومن ثمّ التخلّص منها عند انتفاء الحاجة إليها. تتوفّر CLR بشكل افتراضيّ على معظم أنظمة تشغيل ويندوز (مع الاختلاف بالإصدارات) مثل Windows XP و Windows 7 و Windows 8 و Windows 10 و Windows Phone و Windows Server (بمعظم إصداراته). مكتبة أصناف إطار العمل FCL تحتوي هذه المكتبة الضخمة على آلاف الأصناف classes التي تسمح لنا بإنجاز أيّ مهمّة تخطر ببالنا. فمن التعامل مع النصوص والتعابير النظاميّة regular expressions إلى التعامل مع الاتصالات الشبكيّة network communications والتعامل مع بروتوكولات الانترنت الشهيرة مثل HTTP وFTP وغيرها، والوصول إلى قواعد البيانات والتعامل معها. تتكوّن FCL بدورها من عدّة مكتبات فرعيّة، فهناك مكتبة تساعدنا على تطوير تطبيقات سطح المكتب لويندوز، وأخرى تسمح لنا بتطوير تطبيقات ويب باستخدام تقنيّة ASP.NET، ومكتبة تسمح بإنشاء تطبيقات WCF وهكذا. بمعنى أنّه لن تحتاج إلى التعامل مع كامل مكتبة FCL بل ستنتقي منها ما تحتاجه بحسب متطلّبات عملك، فإذا كنت مطوّر ويب مثلًا فستهتم بالمكتبة الفرعيّة التي تسمح بإنشاء تطبيقات ويب باستخدام ASP.NET. ومن الجدير ذكره أنّ مكتبة FCL تحتوي أيضًا على مكتبة فرعيّة اسمها مكتبة الأصناف الأساسيّة Base Class Library أو اختصارًا BCL، تحتوي هذه المكتبة على الأصناف الأساسيّة التي تعمل مع جميع التطبيقات المنشأة باستخدام المكتبات الأخرى بصرف النظر عن نوعها. مترجم سي شارب C# Compiler لكي نتمكّن من تنفيذ البرامج التي نكتبها باستخدام سي شارب، نحتاج إلى مترجم سي شارب C# Compiler. يكون هذا المترجم مضمّنًا بشكل افتراضيّ مع إطار العمل دون نت. يعمل المترجم على تحويل التعليمات البرمجيّة الموجودة في الشيفرة والمكتوبة بلغة سي شارب إلى تعليمات برمجيّة مكتوبة بلغة تُعرف بلغة مايكروسوفت الوسيطيّة Microsoft Intermediate Language أو اختصارًا MSIL. تُعتبر هذه اللغة الوسيطيّة منخفضة المستوى، وهي شبيهة بلغة Assembly الشهيرة. في الواقع ستعمل بيئة التنفيذ المشتركة CLR على تنفيذ التعليمات المكتوبة بلغة MSIL. فبيئة CLR لا تحتاج أن تعرف لغة البرمجة التي كُتب بها البرنامج أصلًا وهي الميزة الأساسيّة التي تسمح للمبرمج الكتابة بأي لغة برمجة تدعم الدوت نت. فطالما أنّ لغة البرمجة تمتلك مترجمًا متوافقًا مع MSIL فلا مشكلة. وهذا ما جرى مع لغات برمجة أخرى مثل Visual Basic.NET ولغة #F وحتى لغة البايثون Python من خلال IronPython. انظر الشكل التوضيحي التالي: كيف ينفذ البرنامج باستخدام CLR؟ توضع الشيفرة البرمجيّة المكتوبة بلغة سي شارب عادةً في ملفات منفصلة لكلٍّ منها الامتداد cs، يعمل المترجم بعد ذلك على قراءة هذه الملفات التي تسمّى بملفّات النص المصدريّ source code files وتحويل التعليمات البرمجيّة المكتوبة بسي شارب إلى لغة MSIL وجعلها ضمن ملف تنفيذي (الملف MyApp.exe في الشكل السابق). عند تنفيذ البرنامج تقرأ CLR التعليمات البرمجيّة الموجودة ضمن الملف التنفيذي والتي تكون بلغة MSIL وتعمل على تنفيذها عن طريق تحويلها إلى تعليمات برمجيّة أصليّة native code مخصّصة لمعالج الحاسوب الذي يعمل عليه البرنامج حاليًّا. يجري كلّ هذا العمل بسرعة كبيرة جدًّا فعملية التحويل إلى native code لا تجري سوى أوّل مرّة فقط، وبعد ذلك يستفيد البرنامج من السرعة الكبيرة التي توفّرها هذه التعليمات الأصليّة عند التنفيذ. يُفسّر السيناريو السابق سبب قابليّة عمل برامج دوت نت على أيّ نظام تشغيل أو عتاد حاسوبي. فعمليّة كتابة الشيفرة البرمجيّة بأي لغة دوت نت ومن ثمّ تحويلها إلى لغة مايكروسوفت الوسيطيّة MSIL منفصلة تمامًا عن نظام التشغيل وعن العتاد الحاسوبي الذي سيعمل عليه البرنامج. ولعلّ من أبرز الأمثلة على ذلك البرامج المكتوبة بلغة سي شارب والموجّهة إلى أنظمة تشغيل الأجهزة الذكيّة عن طريق منصة Xamarin التي ذكرناها قبل قليل. فالعمل الأساسي الذي قامت به هذه الشركة هو انتاج بيئة CLR منفصلة لكلّ نظام تشغيل مثل Android و iOS مما يسمح للبرامج المكتوبة بلغة سي شارب الخروج من "قمقم" ويندوز إن صحّ التعبير. وأكثر من ذلك، فقد أنتجت مايكروسوفت مؤخّرًا نسخة خاصّة وخفيفة من نظام التشغيل Windows 10 أسمته Windows 10 IoT وهو مخصّص لإنترنت الأشياء. يمكن تنصيب نظام التشغيل هذا بكلّ سلاسة ويُسر على جهاز Raspberry Pi المشهور والموجّه لتطبيقات إنترنت الأشياء، وهذا يعني فتح المجال أمام التطبيقات المكتوبة باستخدام لغة سي شارب للعمل على هذا الجهاز وبالتالي الدخول في هذا المجال المهم والواعد. الخلاصة لغة سي شارب هي لغة عصريّة ولها مستقبل واعد في قطاع الأعمال. فهي اللغة الأساسيّة المستخدمة لكتابة مختلف أنواع التطبيقات الحاسوبيّة ضمن إطار العمل دوت نت. بالإضافة إلى أنّها سلسة وذات بنية مألوفة، ولكن تحتاج إلى قليل من الصبر في تعلّمها. سنتناول في الدروس اللّاحقة كيفيّة كتابة برامج بسيطة ولكن متدرّجة في الصعوبة حتى تكون ملمًّا بأساسيّات هذه اللغة الرائعة.
  3. المتغيرات Variables سبق وأن ذكرنا في الدرس الأوّل أنّه يوجد نمطان أساسيّان لأنواع المتغيّرات في سي شارب، وهما: أنواع قيمة value types وأنواع مرجعيّة reference types. تشتمل أنواع القيمة على الأنواع المُدمجة built-in في اللغة مثل int و float و decimal و double و bool وجميع الأنواع المُعرّفة كبنية struct. سنتحدّث عن البنى في درس لاحق. في حين تشتمل الأنواع المرجعيّة على أيّ نوع آخر وهذا يشتمل على عدد لا يحصى من الأنواع، فيكفيك أن تعرف مثلًا أنّ جميع الأنواع الموجودة في مكتبة FCL هي أنواع مرجعيّة، بالإضافة إلى أنّ أي نوع جديد (على شكل صنف class) ينشئه المستخدم يُعتبر نوعًا مرجعيًّا. ومن المفيد أن تعلم أنّ النوع المُضمّن string هو نوع مرجعيّ أيضًا. يكمن الفرق الأساسي بين أنواع القيمة والأنواع المرجعيّة في مكان وطريقة تخزين قيم المتغيّرات المصرّح عنها بواسطتها. فعند التصريح عن متغيّر من نوع قيمة، تُخزّن أي قيمة يتمّ إسنادها إليه ضمن المتغيّر نفسه أو بمعنى أدق تُخزّن في ذاكرة المُكدّس Stack Memory، أمّا المتغيّر المصرّح عنه على أنّه نوع مرجعيّ فالّذي يُخزّن ضمنه هو العنوان إلى موقع في الذاكرة. هذا الموقع موجود ضمن ما يُسمّى بذاكرة الكَوْمَة Heap Memory. انظر إلى الشكل التوضيحي التالي. حيث صرّحنا عن المتغيّر x من النوع int وخزّنّا القيمة 5 ضمنه. وبما أنّ int هو نوع قيمة، لذلك سيكون المتغيّر مع القيمة المخزّنة فيه ضمن المكدّس Stack. أمّا بالنسبة للمتغيّر s فهو من النوع string وقد أسندنا إليه النص "!Hello" وبما أنّ النوع string هو نوع مرجعيّ كما أسلفنا لذلك فالقيمة التي ستكون مخزّنة ضمن المتغيّر s في الحقيقة ليست النص "!Hello" إنّما العنوان address الذي يُشير إلى موقع ضمن الكومة Heap موجود ضمنه النص "!Hello"، وهذا بالمناسبة ليس سلوكًا تنفرد به سي شارب، بل هو موجود في لغات أخرى مثل ++C و C. سنتوسّع في هذا الموضوع قليلًا عندما نتحدّث عن الأصناف والكائنات لاحقًا في هذه السلسلة. يمكن استخدام أيّ مزيج من الحروف والأرقام عند تسمية المتغيّرات، بشرط أن يكون أوّل محرف في اسم المتغيّر حرفًا وليس رقمًا. كما لا يجوز أن يحتوي اسم المتغيّر على فراغات ولا يجوز أيضًا أن يكون مماثلًا لكلمة محجوزة في سي شارب مثل new أو class أو غيرها، ولا يجوز أن يحتوي على رموزًا خاصّة مثل & و$ و#، ولكن يُعتبر الرمز (_) underscore حرفًا ويجوز الابتداء به. الأنواع المضمنة ومجالاتها الأنواع المُضمّنة هي الأنواع الموجودة ضمنًا في لغة سي شارب وفي إطار عمل دوت نت عمومًا. سنستعرض في الجدول التالي هذه الأنواع. لاحظ أنّ عمود "الاسم" يحتوي على أسماء الأنواع المستخدمة في سي شارب، في حين يحتوي العمود الذي يليه مباشرةً على اسم نفس النوع ولكن ضمن منصّة دوت نت. في الحقيقة يمكننا استخدام أي تسمية نرغبها ولكنّ الأسهل والأفضل هي في استخدام أسماء الأنواع في سي شارب. يعود سبب وجود أسماء أنواع مختلفة في إطار عمل دوت نت هو أنّه بإمكان أي لغة برمجة ضمن منصة دوت نت استخدام نفس هذه الأنواع ضمنها. الاسم النوع الموافق في منصّة دوت نت القيم التي يقبلها الحجم في الذاكرة 1 bool System.Boolean true أو false 2 sbyte System.SByte من128- حتى 127 8 bits 3 byte System.Byte من 0 حتى 255 8 bits 4 short System.Int16 من 32,768- حتى 32,767 16 bits 5 ushort System.UInt16 من 0 حتى 65,535 16 bits 6 int System.Int32 من 2,147,483,648- حتى 2,147,483,647 32 bits 7 uint System.UInt32 من 0 حتى 4,294,967,295 32 bits 8 long System.Int64 من 9,223,372,036,854,775,808- حتى 9,223,372,036,854,775,807 64 bits 9 ulong System.UInt64 من 0 حتى 18,446,744,073,709,551,615 64 bits 10 char System.Char من U+0000 حتى U+ffff 16 bits 11 float System.Single من 3.4*1038- حتى +3.4*1038 32 bits 12 double System.Double من ±5.0*10-324 حتى ±1.7*10308 64 bits 13 decimal System.Decimal (-7.9*1028 to 7.9*1028)/(100 to 28) 128 bits 14 string System.String حسب حدود الذاكرة 15 object System.Object يمكن تخزين بيانات من أيّ نوع ضمن المتغيّرات من النوع object الأنواع من 2 حتى 9 هي أنواع صحيحة لا تقبل أعدادًا ذات فاصلة عشريّة. أما الأنواع من 11 حتى 13 فهي أنواع تقبل أعداد ذات فاصلة عشريّة وتختلف فيما بينها في مجالات الأعداد التي تقبلها ودقّة تلك الأعداد بالنسبة لعدد الخانات على يمين الفاصلة العشريّة. النوع char مخصّص للمتغيّرات التي تسمح بتخزين محرف character واحد فقط، وهو نوع يدعم ترميز Unicode، يُعتبر أي محرف موضوع ضمن علامتي اقتباس مفردتين مثل 'a' من نوع char. في الحقيقة أنّ النوع string يُعبّر عن سلسلة من المحارف من نوع char. النوع object هو الأب العام لجميع الأنواع في إطار العمل دوت نت ومنه تنحدر جميع الأنواع الأخرى مهما كانت، سنصادفه في هذه السلسلة مرّةً أخرى. العوامل Operators تدعم سي شارب نوعين من العوامل بشكل أساسيّ: عوامل أحاديّة unary operators وعوامل ثنائيّة binary operators. سنتحدّث في هذا الدرس عن أكثر العوامل استخدامًا في سي شارب. العوامل الأحادية لهذه العوامل أسبقيّة أعلى في الحساب من العوامل الثنائيّة، وهي تشمل العديد من العوامل يلخّص الجدول التالي أهمّها، انظر إلى الأمثلة العمليّة التي ستأتي بعد الجدول لمعرفة كيفيّة استخدامها: العامل الوصف الاستخدام ! عامل النفي المنطقي وهو عامل يُطبّق على القيم المنطقيّة من النوع bool. x! ~ عامل المتمّم الثنائي bitwise complement وهو عبارة عن عامل نفي ولكن على مستوى البتّات bits. x~ ++ لهذا العامل شكلان يعمل كلّ منها على زيادة قيمة متغيّر عددي بمقدار 1، ويختلفان فقط في توقيت هذه الزيادة. x++ عامل زيادة بادئ. ++x عامل زيادة لاحق. -- لهذا العامل شكلان أيضًا، يعمل كلّ منها على إنقاص قيمة متغيّر عددي بمقدار 1، ويختلفان فقط في توقيت هذا الإنقاص. x-- عامل إنقاص بادئ. --x عامل إنقاص لاحق. (T) وهو عامل التحويل بين الأنواع casting. وهو عامل مهم جدًّا سنصادفه مرارًا في هذه السلسلة. يمكن استبدال الحرف T باسم أيّ نوع يخطر على بالك مثل int وdouble وstring وغيرها. طريقة استخدامه هو في وضع النوع المراد التحويل إليه بين قوسين ونضعها جميعًا قبل القيمة التي نريد تحويلها مثل (int(x لتحويل قيمة x إلى قيمة من النوع int. فهم عاملي الزيادة والإنقاص شغّل برنامج Visual Studio 2015 Community وأنشئ مشروعًا جديدًا من النوع Console Application سمّه UnaryOperatorsTest1 ثم استبدل محتويات الملف Program.cs بالشيفرة التالية: 1 using System; 2 3 4 namespace UnaryOperatorsTest 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 int i = 1; 11 12 Console.WriteLine("Using of pre-increment operator (++i):"); 13 Console.WriteLine("Current value of i is {0}, and after applying ++i, the value of i becomes {1}", i, ++i); 14 Console.WriteLine(new string('-', 40)); 15 16 Console.WriteLine(); 17 i = 1; 18 19 Console.WriteLine("Using of post-increment operator (i++):"); 20 Console.WriteLine("Current value of i is {0}, and after applying i++, the value of i becomes {1}", i, i++); 21 Console.WriteLine(new string('-', 40)); 22 } 23 } 24 } نفّذ البرنامج باستخدام Ctrl+F5 (أو من القائمة Debug > Start Without Debugging) ستحصل على الخرج التالي: sing of pre-increment operator (++i): Current value of i is 1, and after applying ++i, the value of i becomes 2 ---------------------------------------- Using of post-increment operator (i++): Current value of i is 1, and after applying i++, the value of i becomes 1 ---------------------------------------- يوضّح هذا البرنامج البسيط استخدام عامل الزيادة البادئ وعامل الزيادة اللاحق. يبدأ البرنامج بالتصريح عن المتغيّر i من النوع int وإسناد القيمة 1 إليه. تعمل العبارة في السطر 13 على إظهار قيمتين، الأولى هي القيمة الحاليّة للمتغيّر i وتساوي 1، والقيمة الثانيّة هي قيمة المتغيّر i مضافًا إليها 1 باستخدام عامل الزيادة البادئ ++i أي هي القيمة 2، إذًا يقوم هذا العامل بزيادة قيمة المتغيّر i بمقدار 1 قبل تمرير القيمة النهائيّة إلى التابع WriteLine لذلك نحصل على الخرج: Current value of i is 1, and after applying ++i, the value of i becomes 2 ولكن على النقيض من ذلك، نلاحظ أنّ العبارة الموجودة في السطر 20 تعمل على إظهار قيمتين أيضًا، الأولى هي القيمة الحالية للمتغيّر i وتساوي 1 (أعدنا إسناد القيمة 1 للمتغيّر i في السطر 17)، والقيمة الثانيّة هي قيمة المتغيّر i مضافًا إليها 1 باستخدام الزيادة اللاحق i++ ولكن لن تمرَّر القيمة 2 هذه المرّة إلى التابع WriteLine. والسبب في ذلك أنّ البرنامج سيعمل على تمرير قيمة i الأصلية (القيمة 1) ثمّ يطبّق بعد ذلك عامل الزيادة اللاحق. وهذا هو سبب الحصول على الخرج التالي: Current value of i is 1, and after applying i++, the value of i becomes 1 لعلّ هذا السلوك يُسبّب بعض الإرباك للمبرمجين الجدد في سي شارب، وعلى أيّة حال أنصح بتجنّب تمرير القيمة إلى التوابع عمومًا بهذا الأسلوب. إذا احتجت لزيادة (أو إنقاص) قيمة متغيّر ما قبل تمرير لأحد التوابع فاعمل على ذلك ضمن سطر منفصل قبل استدعاء هذا التابع وأرح نفسك. في الحقيقة يُطبّق نفس المفهوم السابق بالنسبة لعامليّ الإنقاص البادئ والإنقاص اللاحق. ملاحظة: انظر إلى طريقة التنسيق الجديدة التي استخدمتها في السطر 13: Console.WriteLine("Current value of i is {0}, and after applying ++i, the value of i becomes {1}", i, ++i); مرّرت إلى التابع WriteLine ثلاثة وسائط: الأوّل هو النص التنسيقي وقد حُجز ضمنه مكانين مخصّصين لقيمتين سأمرّرهما لاحقًا لهذا التابع، هذان المكانان على الشكل {0} و {1}. الوسيط الثاني هو المتغيّر i، والوسيط الثالث هو ++i. سيعمل البرنامج على وضع القيمة الممرّرة للتابع WriteLine والتي تلي النص التنسيقي مباشرةً (في حالتنا هذه قيمة i) في المكان {0}، أمّا المكان {1} فسيُوضع ضمنه القيمة التالية وهي ++i. ينطبق نفس الكلام تمامًا على العبارة الموجودة في السطر 20. كما يحتوي السطران 14 و21 على أسلوب جميل لطباعة سطر فاصل في خرج البرنامج بغرض توضيحه. أنشأنا كائنًا من النوع string باستخدام العامل new ومرّرنا لبانيته وسيطين: الأوّل المحرف '-' من نوع char والثاني القيمة 40: new string('-', 40) سيولّد ذلك نصّا يحتوي على 40 محرف '-' مكرّر (لاحظ علامتي الاقتباس المفردتين ' ')، يمرَّر هذا النص بعد ذلك إلى التابع WriteLine. لا تقلق إن بدا هذا الكلام غير مفهومًا الآن، فسنتحدّث عن الكائنات فيما بعد. فهم عامل النفي المنطقي وعامل التحويل بين الأنواع أنشئ مشروعًا جديدًا من النوع Console Application سمّه UnaryOperatorsTest2 ثم استبدل محتويات الملف Program.cs بالشيفرة التالية: 1 using System; 2 3 4 namespace UnaryOperatorsTest2 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 bool b = true; 11 double d = 8.9; 12 int i; 13 14 Console.WriteLine("b = {0}, !b = {1}", b, !b); 15 16 i = (int)d; 17 Console.WriteLine("d = {0}, after applying casting to (int), i = {1}", d, i); 18 } 19 } 20 } نفّذ البرنامج باستخدام Ctrl+F5 لتحصل على الخرج التالي: b = True, !b = False d = 8.9, after applying casting to (int), i = 8 استخدمنا في هذا البرنامج المتغير b من النوع bool وهو نوع منطقيّ تحمل المتغيّرات المصرّح عنها بواسطته إحدى قيمتين true أو false. أسندنا للمتغيّر b القيمة true عند التصريح عنه في السطر 10، ثمّ عرضنا للمستخدم قيمة b الأصليّة وقيمته بعد تطبيق عامل النفي المنطقي عليه b! لنحصل على الخرج التالي: b = True, !b = False يعكس هذا العامل الحالة المنطقيّة، فإذا كانت true تصبح false، أمّا إذا كانت false فتصبح true. ولكن إذا لاحظت أنّ الخرج يُظهر القيمتين المنطقيتين true و false بحرفين كبيرين في بداية كل منهما: True و False. السبب في ذلك أن التابع WriteLine في السطر 14 يعمل بشكل ضمني على استدعاء التابع ToString لكل من الوسيطين الممرّرين له، أي الوسيطين b و b! فيحصل بذلك على التمثيل النصّي للقيمة المنطقيّة الموجودة في كلّ منهما، والذي يبدأ بحرف طباعي كبير. جرّب استبدال العبارة البرمجيّة في السطر 14 بالعبارة التالية: Console.WriteLine("b = {0}, !b = {1}", b.ToString(), (!b).ToString()); التعديل الذي أجريناه في السطر السابق هو استدعاء التابع ToString بشكل صريح لكلّ وسيط قبل تمريره إلى التابع WriteLine. ستحصل بذلك على نفس الخرج دون أيّ تغيير. بالنسبة لعمليّة التحويل بين الأنواع فقد أجريناها بين المتغيّر d من النوع double (السطر 11) والمتغيّر i من النوع int (السطر 12)، حيث سنحوّل القيمة ذات الفاصلة العشرية 8.9 الموجودة في d إلى قيمة صحيحة بدون فاصلة ونخزّنها ضمن i. تجري عملية التحويل هذه في السطر 16 على الشكل التالي: i = (int)d; لاحظ أنّ القوسين المحيطين بـ int ضروريين. إذا حاولت إزالة عامل التحويل (int) من العبارة السابقة وحاولت تنفيذ البرنامج فستحصل على الخطأ التالي: CS0266 Cannot implicitly convert type 'double' to 'int'. An explicit conversion exists (are you missing a cast?) يُشير هذا الخطأ إلى عدم إمكانيّة إسناد قيمة متغيّر من النوع double إلى متغيّر من النوع int مباشرةً بدون تحويل لأنّ ذلك سيؤدّي إلى ضياع في البيانات (ستضيع القيمة 0.9). يقترح عليك هذا الخطأ استخدام التحويل بين الأنواع cast في الجزء الأخير من الرسالة. أعد وضع عامل التحويل (int) أمام المتغيّر d ونفّذ البرنامج لتحصل في الخرج على ما يلي: d = 8.9, after applying casting to (int), i = 8 انظر كيف أصبحت قيمة i تساوي 8. في الواقع سيصادفنا عامل التحويل كثيرًا في هذه السلسلة. العوامل الثنائية تشتمل هذه العوامل على معظم العوامل الموجودة في سي شارب ولها العديد من الأصناف، تحتاج هذه العوامل إلى وجود مُعاملَين operands على طرفيها لكل تعمل، يلخّص الجدول التالي أهم هذه العوامل مع التصنيف الذي تقع ضمنه. العامل الوصف الاستخدام التصنيف + عملية الجمع العددي x + y عوامل - عملية الطرح العددي x - y حسابيّة * عملية الضرب العددي x * y / عملية القسمة العددية (إذا كان كل من المعاملين من نوع صحيح فسيكون ناتج القسمة صحيحًا بدون فاصلة، حيث تُهمل الأجزاء العشرية في حال وجودها). x / y % عمليّة باقي القسمة x % y > عامل اختبار "أصغر من" يُرجع القيمة true إذا كان المُعامل الأيسر أصغر من الأيمن، وإلّا يُرجع false. x < y عوامل مقارنة < عامل اختبار "أكبر من" يُرجع القيمة true إذا كان المُعامل الأيسر أكبر من الأيمن، وإلّا يُرجع false. x > y => عامل اختبار "أصغر من أو يساوي" يُرجع القيمة true إذا كان المُعامل الأيسر أصغر من أو يساوي الأيمن، وإلّا يُرجع false. x <= y =< عامل اختبار "أكبر من أو يساوي" يُرجع القيمة true إذا كان المُعامل الأيسر أكبر من أو يساوي الأيمن، وإلّا يُرجع false. x >= y == عامل اختبار "المساواة" بين قيمتين، يُرجع true إذا كانت القيمتين متساويتين وإلّا يُرجع false. x == y عوامل اختبار المساواة =! عامل اختبار "عدم المساواة" بين قيمتين، يُرجع true إذا كانت القيمتين غير متساويتين وإلّا يُرجع false. x != y && تطبيق منطق AND على قيمتين (أو تعبيرين) منطقيين. x && y العوامل || تطبيق منطق OR على قيمتين (أو تعبيرين) منطقيين. x || y الشرطية = عامل الإسناد للقيمة (أو التعبير) الموجودة في اليمين إلى المتغيّر الموجود في اليسار. x = y عوامل إسناد =+ عامل الجمع ثم الإسناد. x += y =- عامل الطرح ثم الإسناد. x -= y =* عامل الضرب ثم الإسناد. x *= y =/ عامل القسمة ثم الإسناد. x /= y =% عامل باقي القسمة ثم الإسناد. x %= y فهم العوامل الحسابية تُعتبر هذه العوامل بسيطة وواضحة وهي مشتركة بين جميع لغات البرمجة. على أيّة حال إليك برنامجًا بسيطًا يتعامل معها ويوضّح وظائفها. 1 using System; 2 3 namespace ArithmeticOperators 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 int x, y; 10 string str_x, str_y; 11 12 //input operands. 13 Console.Write("Input left operand (x) : "); 14 str_x = Console.ReadLine(); 15 16 Console.Write("Input right operand (y) : "); 17 str_y = Console.ReadLine(); 18 19 //convert each operand to integer representative. 20 x = int.Parse(str_x); 21 y = int.Parse(str_y); 22 23 24 Console.WriteLine(); 25 26 //perform arithmetic calculations and display results. 27 Console.WriteLine("x + y = {0}", x + y); 28 Console.WriteLine("x - y = {0}", x - y); 29 Console.WriteLine("x * y = {0}", x * y); 30 Console.WriteLine("x / y = {0}", x / y); 31 Console.WriteLine("x % y = {0}", x % y); 32 33 } 34 } 35 } نفّذ البرنامج باستخدام Ctrl+F5. سيطلب منك البرنامج إدخال المُعامل الأيسر left operand، ثم المُعامل الأيمن right operand، وبعدها ينفّذ العمليّات الحسابيّة الأربع عليهما. جرّب إدخال القيمتين 9 و 2 على الترتيب لتحصل على الخرج التالي: Input left operand (x) : 9 Input right operand (y) : 2 x + y = 11 x - y = 7 x * y = 18 x / y = 4 x % y = 1 العمليّات الثلاث الأولى واضحة. بالنسبة لعمليّة القسمة يجب أن يكون الناتج 4.5، ولكن بما أنّ عملية القسمة تجري بين قيمتين صحيحتين فإنّ النتيجة يجب أن تكون صحيحة، وبالتالي يُهمل الجزء العشري 0.5 ويكون الناتج 4 فقط. بالنسبة لعمليّة باقي القسمة x % y فإنّ النتيجة 1 هي باقي قسمة 9 على 2. ملاحظة: إذا لم ترغب بحذف الجزء العشري من ناتج عملية القسمة الصحيحة ودون أن تغيّر أنوع المتغيّرات، يمكنك استخدام عامل التحويل بين الأنواع (T). استبدال العبارة الموجودة في السطر 30 بالعبارة التالية: Console.WriteLine("x / y = {0}", x /(double)y); وضعت عامل التحول (double) قبل المتغيّر y لتحويل قيمته العدديّة إلى قيمة من نوع double (دون المسّ بقيمة y الأصليّة بالطبع)، فعندما يرى البرنامج أنّه يُجري عملية القسمة بين قيمة صحيحة (قيمة x) وقيمة من النوع double فسيعطي الناتج على شكل قيمة من نوع double تُمرّر بدورها إلى التابع WriteLine ليعرض القيمة 4.5 بدلًا من 4. ويمكن فعل نفس الأمر مع المتغيّر x بدلًا من y إذا أحببت. فهم عوامل المقارنة سنتناول عوامل المقارنة > و < و => و =< و == و =! في البرنامج التالي: 1 using System; 2 3 4 namespace RelationalOperators 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 int x, y; 11 string str_x, str_y; 12 13 14 //input operands. 15 Console.Write("Input left operand : "); 16 str_x = Console.ReadLine(); 17 18 Console.Write("Input right operand : "); 19 str_y = Console.ReadLine(); 20 21 //convert each operand to integer representative. 22 x = int.Parse(str_x); 23 y = int.Parse(str_y); 24 25 Console.WriteLine(); 26 27 //perform comparing operations and display results. 28 Console.WriteLine("{0} == {1} evaluates to {2}", x, y, x == y); 29 Console.WriteLine("{0} != {1} evaluates to {2}", x, y, x != y); 30 Console.WriteLine("{0} > {1} evaluates to {2}", x, y, x > y); 31 Console.WriteLine("{0} >= {1} evaluates to {2}", x, y, x >= y); 32 Console.WriteLine("{0} < {1} evaluates to {2}", x, y, x < y); 33 Console.WriteLine("{0} <= {1} evaluates to {2}", x, y, x <= y); 34 } 35 } 36 } نفّذ البرنامج وأدخل القيمتين 3 و 4 على الترتيب لتحصل على الخرج التالي: Input left operand : 3 Input right operand : 4 3 == 4 evaluates to False 3 != 4 evaluates to True 3 > 4 evaluates to False 3 >= 4 evaluates to False 3 < 4 evaluates to True 3 <= 4 evaluates to True تكون نتيجة تنفيذ عوامل المقارنة قيمة منطقية true أو false. جرّب إدخال قيم متنوّعة، كما جرّب إدخال قيمتين متساويتين وانظر إلى الخرج. فهم العوامل الشرطية العاملين الشرطيين && (AND) و || (OR) هما عاملان مهمّان جدًّا ويستخدمان بكثرة في بنى القرار في سي شارب. ولهما وجود في جميع لغات البرمجة. يوضّح البرنامج التالي استخدام هذين العاملين بصورة مبسّطة. 1 using System; 2 3 4 namespace RelationalOperators 5 { 6 class Program 7 { 8 static void Main(string[] args) 9 { 10 int a, b, c, d; 11 bool and_operator, or_operator; 12 13 a = 1; 14 b = 2; 15 c = 5; 16 d = 9; 17 18 and_operator = (a > b) && (c <= d); 19 Console.WriteLine("({0} > {1}) && ({2} <= {3}) evaluates to {4}", a, b, c, d, and_operator); 20 21 or_operator = (a > b) || (c <= d); 22 Console.WriteLine("({0} > {1}) || ({2} <= {3}) evaluates to {4}", a, b, c, d, or_operator); 23 } 24 } 25 } لا نستخدم العوامل الشرطيّة بهذا الأسلوب في البرامج الحقيقيّة، ولكنّ هذا الأسلوب مفيد في توضيح آلية عمل العوامل الشرطيّة وتفاعلها مع عوامل المقارنة. نفّذ البرنامج لتحصل على الخرج التالي: (1 > 2) && (5 <= 9) evaluates to False (1 > 2) || (5 <= 9) evaluates to True تفسير هذا الخرج يسير للغاية. لنبدأ بالسطر الأوّل، نتيجة حساب التعبير الأول هو false: (1 > 2) && (5 <= 9) وسبب ذلك هو أنّ نتيجة التعبير (2 < 1) هو false، أمّا نتيجة حساب (9 => 5) هو true وبالتالي سيعمل العامل الشرطي && بالنتيجة على حساب التعبير false && true والذي يعطي بكلّ تأكيد القيمة المنطقية false. بالنسبة للسطر الثاني، وهو التعبير: (1 > 2) || (5 <= 9) والذي يعطي true. والسبب هو أنّ العامل الشرطي || سيعمل على حساب التعبير false || true والذي يعطي القيمة المنطقيّة true. لاحظ استخدام الأقواس على أطراف عوامل المقارنة، يمكن الاستغناء عنها، ولكن لا أنصح بذلك، استخدم الأقواس دومًا حتى ولو لم يكن استخدامها ضروريًا لتوضيح منطق البرنامج، ولكن استخدمها بحكمة. السبب في انتفاء الحاجة إلى استخدام الأقواس في هذا البرنامج، هو أنّ عوامل المقارنة لها أسبقيّة تنفيذ أعلى من العوامل الشرطيّة، لذلك فهي تُقيّم قبل تقييم العوامل الشرطيّة. فهم عوامل الإسناد استخدمنا حتى الآن عامل الإسناد (=). توجد عوامل إسناد أخرى تُسهّل البرمجة في سي شارب وهي =+ و =- و =* و =/ و =%. الأمر بسيط، بالنسبة لعامل الإسناد =+ يمكن توضيح عمله بالشيفرة التالية: int x = 3; x += 5; بعد تنفيذ الشيفرة السابقة ستصبح قيمة x تساوي 8. لأنّ العبارة x += 5 تكافئ تمامًا العبارة x = x + 5 ويمكننا استبدالها بها. يُطبّق نفس الأسلوب تمامًا على العوامل الباقية. فمثلًا انظر إلى الشيفرة التالية: int x = 21; x /= 4; x %= 3; هل تستطيع تخمين قيمة x بعد تنفيذ هذه الشيفرة؟ إذا كانت النتيجة 2 فقد أصبت. السبب في ذلك بسيط. فقد بدأنا بقيمة x تساوي 21 ثم نفّذنا العبارة x /= 4 التي تكافئ العبارة x = x / 4 وهي قسمة صحيحة، لذلك سيحمل x القيمة 5 (بدون فاصلة عشرية). بعد تنفيذ العبارة الأخيرة x %= 3 التي تكافئ العبارة x = x % 3 ستصبح قيمة x تساوي 2 لأنّ باقي قسمة 5 على 3 يساوي 2. وهذا كلّ ما في الأمر. تمارين داعمة تمرين 1 حاول تخمين القيمة المنطقيّة التي ستُطبع على الشاشة باستخدام القلم والورقة فقط: int a = 30; a /= 3; a %= 3; Console.WriteLine(a == 1); تمرين 2 حاول تخمين قيمة f التي ستُطبع على الشاشة باستخدام القلم والورقة فقط: int x; double f; x = 9; f = (double)x / 2; f *= 10; Console.WriteLine("f = {0}", f); الخلاصة لفد تعرّفنا في هذا الدرس على الأنواع المُضمّنة في سي شارب وعلى مجالات كلٍّ منها، وعلى الفرق الأساسي بين أنواع القيمة value types والأنواع المرجعيّة reference types. كما تحدّثنا عن معظم العوامل التي تدعمها سي شارب وتصنيفاتها. وتناولنا بعض الأمثلة التوضيحيّة على استخدامها. سنتحدّث في الدرس التالي عن بنى القرار وتغيير مسار البرنامج وهو موضوع مهم في جميع لغات البرمجة.
  4. تحدثنا في الدرس السابق عن المبادئ الأوليّة لتطبيق مفاهيم البرمجة كائنيّة التوجّه في سي شارب، حيث تعلّمنا كيفيّة إنشاء الأصناف وأعضائها، وإنشاء الكائنات من الأصناف والتعامل معها. سنتابع في هذا الدرس الحديث عن تطبيق مبادئ البرمجة كائنيّة التوجّه في سي شارب حيث سنتناول مُحدّد الوصول private (تناولنا محدّد الوصول public في الدرس السابق). وسنتحدّث أيضًا عن الخصائص Properties كنوع جديد من أعضاء الصنف، والفرق بينها وبين حقول البيانات Data Fields. وسنختم هذا الدرس بالحديث عن الأعضاء الساكنة Static Members في الصنف. الخصائص Properties للخصائص ولحقول البيانات المعنى المنطقيّ نفسه في البرمجة كائنيّة التوجّه، إلّا أنّ سي شارب تميّز بينهما من الناحية العمليّة. للخصائص في سي شارب مرونة أكبر، حيث من الممكن تنفيذ عبارات برمجيّة عند إسناد قيمة إلى خاصيّة أو حتى عند القراءة منها. كما من الممكن أن نجعل إحدى الخواص قابلة للقراءة فقط أو حتى قابلة للكتابة فقط (ولو أنّه أمر نادر الحدوث). لفهم هذه المزايا بشكل جيّد سأستعير البرنامج Lesson06_02 من الدرس السابق، وأجري عليه بعض التعديلات لإدخال مفهوم الخصائص. 1 using System; 2 3 namespace Lesson07_01 4 { 5 6 class Employee 7 { 8 private string firstName; 9 private string lastName; 10 private double salary; 11 12 public string FirstName 13 { 14 get 15 { 16 return this.firstName; 17 } 18 set 19 { 20 this.firstName = value; 21 } 22 } 23 24 public string LastName 25 { 26 get 17 { 28 return this.lastName; 29 } 30 set 31 { 32 this.lastName = value; 33 } 34 } 35 36 37 public double Salary 38 { 39 get 40 { 41 return this.salary; 42 } 43 set 44 { 45 this.salary = value; 46 } 47 } 48 49 public string DisplayInfo() 50 { 51 string result = string.Format("{0} {1} - Salary: {2:N0}", 52 this.FirstName, this.LastName, this.Salary); 53 54 return result; 55 } 56 57 public Employee(string firstName, string lastName, double salary) 58 { 59 this.FirstName = firstName; 60 this.LastName = lastName; 61 this.Salary = salary; 62 } 63 64 public Employee() 65 { 66 67 } 68 } 69 70 71 class Program 72 { 73 static void Main(string[] args) 74 { 75 Employee employee1, employee2; 76 77 employee1 = new Employee("Mohammad", "Mansoor", 1000); 78 employee2 = new Employee("Saleh", "Mahmoud", 2500); 79 80 Console.WriteLine("First Employee: {0}", employee1.DisplayInfo()); 81 Console.WriteLine("Second Employee: {0}", employee2.DisplayInfo()); 82 } 83 } 84 } عند تنفيذ البرنامج Lesson07_01 سنحصل على نفس الخرج الذي حصلنا عليه في البرنامج Lesson06_02. لقد أجرينا في الحقيقة بعض التعديلات التي تبدو للوهلة الأولى أنّها ليست ذات مغزى. لقد استبدلنا محدّد الوصول للحقول في الأسطر من 8 حتى 10 ليصبح private بدلًا من public (كما كان الوضع في البرنامج Lesson06_02). يفيد مُحدّد الوصول هذا في جعل الحقل خاصًّا بالصنف ولا يمكن الوصول إليه من خارج الكائن المُنشَأ من هذا الصنف، وبالتالي لا يمكن لأحد أن يُعدّل عليه إلّا التوابع الموجودة ضمن نفس الصنف حصرًا. الأمر الآخر أنّنا قد جعلنا أسماء الحقول تبدأ بحرف طباعي صغير وذلك لتمييزها عن الخصائص التي ستأتي بعدها والتي تحمل نفس الاسم ولكن بحرف طباعي كبير. يبدأ التصريح عن الخاصيّة FirstName في السطر 12 ويمتدّ حتى السطر 22. للخصائص في سي شارب فوائد عظيمة سنختبرها بالتدريج في هذا الدرس وفي الدروس اللّاحقة. لاحظ وجود النوع string قبل اسم الخاصيّة في السطر 12، يُشير ذلك إلى أنّ هذه الخاصيّة تقبل وتعطي قيمًا نصيّة فحسب. في الحقيقة يمكن استبدال string بأيّ نوع نحتاجه. من الواضح أنّ التصريح عن الخاصيّة FirstName يتألّف من قسمين: قسم القراءة get (بين السطرين 14 و 17) وقسم الإسناد set (بين السطرين 18 و 21). في الواقع لا تتعدّى الخاصيّة كونها وسيلة للوصول إلى الحقول الخاصّة private fields الموجودة ضمن الكائن سواءً بالقراءة أو الإسناد، ولكن مع إمكانيّة معالجة القيم سواءً قبل إسنادها إلى هذه الحقول أو بعد القراءة منها. عندما نحاول إسناد قيمة إلى الخاصيّة FirstName ستُنفّذ العبارات البرمجيّة الموجودة في قسم set، وهي عبارة برمجيّة واحدة فقط في مثالنا هذا: this.firstName = value; الكلمة value هي كلمة محجوزة تحتوي على القيمة المُسندة إلى الخاصيّة FirstName. العبارة السابقة واضحة للغاية فهي تعمل على إسناد القيمة المخزّنة ضمن value إلى الحقل firstName. لاحظ كيف يمكننا الوصول إلى هذا الحقل من الكلمة this، كما ويمكننا إغفالها. نستطيع الوصول إلى الحقل firstName رغم أنّه ذو محدّد وصول private لأنّنا نصل إليه من تابع يقع في نفس الصنف. أمّا عندما نحاول قراءة الخاصيّة FirstName فسيتمّ تنفيذ العبارات البرمجيّة الموجودة ضمن القسم get. في هذا المثال يحتوي القسم get على عبارة برمجيّة واحدة وهي: return this.firstName; حيث تعمل على إرجاع القيمة المخزّنة ضمن الحقل firstName إلى الشيفرة التي طلبت قراءة الخاصيّة FirstName. يطبّق نفس الأمر تمامًا على الخاصيّتين LastName و Salary. الخصائص المطبقة تلقائيا يبدو البرنامج السابق طويلًا بلا مبرّر، فنحن لم نقم بأيّ عمل ضمن الخصائص سوى الإسناد أو القراءة. إذا كان الأمر كذلك في برامجك الحقيقيّة فيمكنك الاستغناء عن هذا الشكل من الخصائص واللجوء إلى شكل أكثر حداثةً وعصريّة، والذي يتمثّل بالخصائص المطبّقة تلقائيًّا auto implemented properties. انظر الشكل العام لها فيما يتعلّق بالخاصيّة FirstName: public string FirstName { get; set; } لم تعد العبارات البرمجيّة في قسميّ get و set موجودة. ينحصر دور هذه الخاصيّة في شكلها الحالي في تخزين القيم ضمن الخاصيّة FirstName والقراءة منها فقط. ولكن يأتي السؤال هنا، أين ستخزّن الخاصيّة FirstName قيمها، فأنا لا أرى حقلًا للتخزين! يعمل المترجم في هذه الحالة على إنشاء حقل خاص غير مُشاهد في شيفرة MSIL وظيفته الاحتفاظ بقيمة الخاصيّة FirstName. وهنا قد يجول بخاطرك سؤال آخر: لماذا كلّ هذا التعقيد، لماذا لا نستخدم الحقول كما كنّا نفعل في الدرس السابق وحسب؟ الإجابة بسيطة على هذا التساؤل المشروع. فنحن نستخدم الخصائص بهذا الشكل لغايات تصميميّة فحسب. فكلّما كنت بحاجة لأن تُضيف خاصيّة لأحد الأصناف يمكن الوصول إليها من خارجه فافعل ذلك عن طريق الخصائص (وليس الحقول) ولن تندم، وإن بدا ذلك يتطلّب المزيد من العمل. سنطبّق هذه الخصائص الفريدة على برنامجنا المعدّل Lesson07_02 بحيث تستغني تمامًا عن الحقول firstName و lastName و salary. 1 using System; 2 3 namespace Lesson07_02 4 { 5 6 class Employee 7 { 8 public string FirstName { get; set; } 9 public string LastName { get; set; } 10 public double Salary { get; set; } 11 12 public string DisplayInfo() 13 { 14 string result = string.Format("{0} {1} - Salary: {2:N0}", 15 this.FirstName, this.LastName, this.Salary); 16 17 return result; 18 } 19 20 public Employee(string firstName, string lastName, double salary) 21 { 22 this.FirstName = firstName; 23 this.LastName = lastName; 24 this.Salary = salary; 25 } 26 27 public Employee() 28 { 29 30 } 31 } 32 33 34 class Program 35 { 36 static void Main(string[] args) 37 { 38 Employee employee1, employee2; 39 40 employee1 = new Employee("Mohammad", "Mansoor", 1000); 41 employee2 = new Employee("Saleh", "Mahmoud", 2500); 42 43 Console.WriteLine("First Employee: {0}", employee1.DisplayInfo()); 44 Console.WriteLine("Second Employee: {0}", employee2.DisplayInfo()); 45 } 46 } 47 } أصبح هذا البرنامج الآن يُشبه البرنامج Lesson06_02 من الدرس السابق إلى حدّ كبير، باستثناء أنّنا نستخدم هنا الخصائص بدلًا من الحقول. لاحظ فقط أنّه يمكننا كتابة التصريح عن أيّ خاصيّة على نفس السطر مثل الأسطر 8 و 9 و 10. الخصائص ذات إمكانية القراءة فقط هل تذكُر التمرين الداعم الأوّل من الدرس السابق؟ كان يطلب ذلك التمرين إضافة تابع جديد اسمه GetSalaryAfterTax للحصول على قيمة الراتب بعد خصم الضريبة. واتفقنا وقتها أن تكون هذه الضريبة 2%. سنضيف خاصيّةً لتقوم بهذه المهمّة بدلًا من هذا التابع، ولكنّنا سنجعلها للقراءة فقط read only. أي لا يمكن إسناد أي قيم لها. ستكون الخاصيّة SalaryAfterTax الجديدة على الشكل التالي: public double SalaryAfterTax { get { return 0.98 * this.Salary; } } من الواضح أنّه قد أزلنا القسم set المسؤول عن الإسناد من تصريح الخاصيّة SalaryAfterTax وبذلك تتحوّل للقراءة فقط. يحتوي القسم get على عمليّة حسابيّة بسيطة تطبّق عملية حسم الضريبة على الراتب Salary. سنجري تعديلًا طفيفًا على التابع DisplayInfo لكي يُرفق قيمة الراتب بعد حسم الضريبة ضمن النصّ المنسّق الذي يرجعه. سنحصل في النتيجة على البرنامج Lesson07_03 المعدّل: 1 using System; 2 3 namespace Lesson07_03 4 { 5 6 class Employee 7 { 8 public string FirstName { get; set; } 9 public string LastName { get; set; } 10 public double Salary { get; set; } 11 public double SalaryAfterTax 12 { 13 get 14 { 15 return 0.98 * this.Salary; 16 } 17 } 18 19 public string DisplayInfo() 20 { 21 string result = string.Format("{0} {1} \n Salary: {2:N0} \n Salary after tax: {3:N0}", 22 this.FirstName, this.LastName, this.Salary, this.SalaryAfterTax); 23 24 return result; 25 } 26 27 public Employee(string firstName, string lastName, double salary) 28 { 29 this.FirstName = firstName; 30 this.LastName = lastName; 31 this.Salary = salary; 32 } 33 34 public Employee() 35 { 36 37 } 38 } 39 40 41 class Program 42 { 43 static void Main(string[] args) 44 { 45 Employee employee1, employee2; 46 47 employee1 = new Employee("Mohammad", "Mansoor", 1000); 48 employee2 = new Employee("Saleh", "Mahmoud", 2500); 49 50 Console.WriteLine("First Employee: {0}", employee1.DisplayInfo()); 51 Console.WriteLine("Second Employee: {0}", employee2.DisplayInfo()); 52 } 53 } 54 } أضفت الخاصيّة SalaryAfterTax (الأسطر من 11 حتى 17). وأجريت تعديلًا طفيفًا ضمن التابع DisplayInfo في السطر 21 حيث أضفت المحرف n\ والذي يُستخدم ضمن النص للإشارة إلى وجوب الانتقال إلى سطر جديد لأغراض تنسيقية فقط، كما أضفت مكانًا في النصّ التنسيقيّ {3:N0} لإدراج قيمة الراتب بعد خصم الضريبة this.SalaryAfterTax، وهذا كلّ ما في الأمر. إذا حاولت في هذا البرنامج أن تُسند أيّ قيمة إلى الخاصيّة SalaryAfterTax ستحصل على خطأ يفيد أنّها للقراءة فقط read only. الأعضاء الساكنة Static Members هي أعضاء يمكن استدعاؤها مباشرةً من الصنف الذي صُرّحت ضمنه، وليس من كائن مُنشَأ من هذا الصنف. يمكن أن نجعل أيّ عضو ساكن وذلك بوسمه بالكلمة المحجوزة static. يوضّح البرنامج Lesson07_04 استخدام التوابع الساكنة. لاحظ وجود الكلمة المحجوزة static بعد محّدد الوصول public: 1 using System; 2 3 namespace Lesson07_04 4 { 5 class Calculator 6 { 7 public static double Addition(double x, double y) 8 { 9 return x + y; 10 } 11 12 public static double Minus(double x, double y) 13 { 14 return x - y; 15 } 16 17 public static double Division(double x, double y) 18 { 19 if (y == 0) 20 { 21 return double.NaN; 22 } 23 else 24 { 25 return x / y; 26 } 27 } 28 29 public static double Multiplication(double x, double y) 30 { 31 return x * y; 32 } 33 } 34 35 class Program 36 { 37 static void Main(string[] args) 38 { 39 double x = 5; 40 double y = 9; 41 42 double addition = Calculator.Addition(x, y); 43 double minus = Calculator.Minus(x, y); 44 double multiplication = Calculator.Multiplication(x, y); 45 double division = Calculator.Division(x, y); 46 47 Console.WriteLine("{0} + {1} = {2}", x, y, addition); 48 Console.WriteLine("{0} - {1} = {2}", x, y, minus); 49 Console.WriteLine("{0} * {1} = {2}", x, y, multiplication); 50 Console.WriteLine("{0} / {1} = {2}", x, y, division); 51 } 52 } 53 } نفّذ البرنامج السابق لتحصل على الخرج التالي: 5 + 9 = 14 5 - 9 = -4 5 * 9 = 45 5 / 9 = 0.555555555555556 أنشأنا الصنف Calculator الذي يحتوي على التوابع الساكنة Addition و Minus و Multiplication و Division. إذا انتقلنا إلى التابع Main (الذي هو بالمناسبة تابع ساكن بسبب وجود الكلمة static) انظر إلى السطر 42 كيف استدعينا التابع Addition من الصنف Calculator مباشرةً بدون إنشاء أي كائن من هذا الصنف. سنكرّر نفس العمليّة من أجل التوابع Minus و Multiplication و Division. أمرٌ أخير. انظر إلى محتوى التابع الساكن Division، ستجد أنّنا نختبر قيمة y فيما إذا كانت تساوي الصفر أم لا. فإذا كانت قيمة y تساوي الصفر فإنّه لا يجوز القسمة على صفر. لذلك فنرجع double.NaN وهو عبارة عن ثابت يُعبّر عن عدم وجود قيمة عدديّة. وهذا أمر طبيعي لأنّ القسمة على صفر لن تعطينا عدد. إذا استبدلت قيمة y في السطر 40 من البرنامج السابق بالقيمة 0 سنحصل على الخرج التالي: 5 + 0 = 5 5 - 0 = 5 5 * 0 = 0 5 / 0 = NaN لاحظ السطر الأخير من البرنامج كيف يبدو منطقيًّا تمامًا. يمكننا تعميم نفس المفهوم السابق بالنسبة للخصائص والحقول ضمن الصنف بجعلها ساكنة وذلك بإضافة الكلمة المحجوزة static بعد محدّد الوصول مباشرةً. بقي أن نشير إلى أنّه من غير الممكن استخدام الكلمة المحجوزة this ضمن أي عضو ساكن والسبب كما أعتقد واضح. يُشير this إلى الكائن الذي يحدث من ضمنه الاستدعاء. ولكن في الأعضاء الساكنة فإنّنا نجري الاستدعاءات للتوابع أو الخصائص من الصنف المصرّحة ضمنه مباشرةً، لذلك فاستخدام this لن يكون له أيّ معنى. تمارين داعمة تمرين 1 أجرِ تعديلًا على البرنامج Lesson07_03 بحيث يُضيف الخاصيّة Tel (قابلة للقراءة وللكتابة) التي تمثّل رقم الهاتف للموظّف Employee، ثمّ أجرِ التعديل المناسب على التابع DisplayInfo لكي يعرض قيمة هذه الخاصيّة على سطر منفصل كما يفعل مع بقيّة الخصائص. تمرين 2 أنشئ صنفًا سمّه StaticDemo بحيث يحتوي على خاصيّة ساكنة اسمها Counter من النوع int. عند كل إنشاء لكائن من هذا الصنف يجب زيادة قيمة هذه الخاصيّة بمقدار 1 وبشكل تلقائيّ. (تلميح: يمكنك كتابة العبارة البرمجيّة المسؤولة عن زيادة قيمة الخاصيّة Counter ضمن بانيّة الصنف StaticDemo التي ليس لها وسائط.) الخلاصة تعرّفنا في هذا الدرس على كيفيّة التعامل مع الخصائص، والتي تمثّل أسلوبًا أكثر تطوّرًا من الحقول لتشكّل مزايا الصنف. كما اتفقنا أنّه ينبغي على الحقول أن تكون داخليّة بالنسبة للصنف باستخدام محدّد الوصول private، وتعرّفنا أيضًا على الأعضاء الساكنة static members وكيفيّة التعامل معها.
  5. تُعتبر الحلقات التكراريّة من البنى المهمّة في لغات البرمجة، حيث نستطيع من خلالها تنفيذ عبارة أو عدّة عبارات برمجيّة لعدد من المرّات. تدعم سي شارب مثل باقي لغات البرمجة نوعين من الحلقات التكراريّة من حيث عدد التكرار، فهناك الحلقات ذات العدد المحدّد من المرّات (حلقة for) والتي نعلم فيها عدد مرّات التكرار بشكل مسبق، والحلقات ذات العدد غير المحدّد من المرّات (حلقة do-while وحلقة while) التي يكون فيها عدد مرّات التكرار غير مُحدّدًا. حلقة for التكرارية يمكن من خلال هذه الحلقة تكرار تنفيذ عبارة برمجيّة أو أكثر عددًا محدّدًا من المرّات، ولهذه الحلقة الشكل العام التالي: for ([init_counter]; [loop_condition]; [counter_expression]) { statement1; statement2; ... } القسم [init_counter] هو قسم التهيئة الذي يتمّ من خلاله تهيئة متغيّر الحلقة بقيمة ابتدائيّة (وغالبًا ما يتمّ التصريح عنه في هذا القسم أيضًا)، يسمّى متغيّر الحلقة أيضًا بعدّاد الحلقة loop counter، أمّا القسم [loop_condition] فيمثّل شرط التكرار أو الاستمرار للحلقة، فهو تعبير مقارنة يعطي true أو false بحيث تستمرّ الحلقة بالتكرار طالما كان هذا الشرط محقّقًا (يعطي true). القسم الأخير [counter_expression] ويتمّ فيه عادةً إجراء عملية حسابية على متغيّر الحلقة وغالبًا ما تكون هذه العمليّة هي زيادة متغيّر الحلقة بمقدار واحد. انظر الشيفرة البسيطة التالية التي تعمل على تنفيذ العبارة التي تحوي التابع WriteLine ثلاث مرّات: for (int i = 0; i < 3; i++) { Console.WriteLine("i = {0}", i); } عند تنفيذ الشيفرة السابقة ستحصل على الخرج التالي: i = 0 i = 1 i = 2 لاحظ أنّنا صرّحنا عن المتغيّر i من النوع int وأسندنا إليها القيمة 0 في قسم التهيئة، وبالنسبة لشرط الاستمرار للحلقة i < 3 فمن الواضح أنّ الحلقة ستستمرّ بالتكرار طالما كانت قيمة i أصغر تمامًا من 3. أمّا بالنسبة للقسم الأخير فنعمل على زيادة قيمة متغيّر الحلقة i بمقدار 1 في كلّ دورة عن طريق عامل الزيادة اللاحق ++i. آلية عمل هذه الحلقة بسيطة: عندما يصل تنفيذ البرنامج إلى حلقة for يتمّ التصريح عن المتغيّر i وإسناد القيمة 0 إليه. يختبر البرنامج شرط استمرار الحلقة i < 3 فإذا كان true يبدأ بتنفيذ العبارات البرمجيّة الموجودة ضمن حاضنة for. وإلّا يخرج فورًا من الحلقة. بعد الانتهاء من تنفيذ العبارات ضمن حاضنة for، ينتقل البرنامج إلى التعبير ++i ليزيد قيمة i بمقدار 1. تتكرّر نفس الخطوتين 2 و 3. سيتكرّر في هذا المثال البسيط تنفيذ العبارة الموجودة في الحاضنة ثلاث مرّات لأنّ العدّ يبدأ من الصفر (قيمة i الابتدائيّة تساوي الصفر). لنتناول الآن برنامجًا عمليًّا وظيفته إيجاد مجموع سلسلة من الأعداد المتتالية. أنشئ برنامجًا جديدًا اسمه Lesson04_1 ثمّ استبدل محتويات الملف Program.cs بالشيفرة التالية: 1 using System; 2 3 namespace Lesson04_1 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 int n, sum = 0; 10 string str_n; 11 12 Console.WriteLine("This Program calculates the series: sum = 1 + 2 + 3 + ... + n"); 13 Console.Write("Input 'n' please: "); 14 str_n = Console.ReadLine(); 15 16 n = int.Parse(str_n); 17 18 for (int i = 1; i <= n; i++) 19 { 20 sum += i; 21 } 22 23 Console.WriteLine("sum = {0}", sum); 24 } 25 } 26 } يعمل البرنامج السابق على جمع الأعداد من 1 حتى القيمة المدخلة n. أي سيحسب برنامجنا مجموع السلسلة: 1 + 2 + 3 + … + n. لاحظ القيمة الابتدائيّة للمتغيّر i (تساوي 1) وشرط استمرار الحلقة i <= n في السطر 18. تذكّر أنّ العبارة الموجودة في السطر 20 تُكافئ العبارة sum = sum + i. نفّذ البرنامج وجرّب إدخال قيم مختلفة للمتغيّر n لتحصل على المجاميع الموافقة. ملاحظة: المتغيّر i في البرنامج السابق مرئي فقط ضمن الحلقة التكراريّة ولا وجود له خارجها، يعرف هذا بمجال الرؤية للمتغيّر variable scope. ستؤدّي محاولة الوصول للمتغيّر i خارج الحلقة إلى خطأ أثناء بناء البرنامج. سنكتب الآن برنامجًا آخرًا لحساب مجموع السلسلة: 2 + 4 + 6 + 8 + … + n. لن يختلف البرنامج في هذا المثال عن البرنامج Lesson04_1 باستثناء أنّنا سنجمع الأعداد الزوجية فقط. إليك البرنامج كما سيبدو: 1 using System; 2 3 namespace Lesson04_2 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 int n, sum = 0; 10 string str_n; 11 12 Console.WriteLine("This Program calculates the series: sum = 2 + 4 + 6 + ... + n"); 13 Console.Write("Input 'n' please: "); 14 str_n = Console.ReadLine(); 15 16 n = int.Parse(str_n); 17 18 for(int i = 0; i <= n; i += 2) 19 { 20 sum += i; 21 } 22 23 Console.WriteLine("sum = {0}", sum); 24 } 25 } 26 } لاحظ كيف نزيد قيمة المتغيّر i في كلّ تكرار للحلقة for بمقدار 2 باستخدام التعبير i += 2 (السطر 18)، وبالتالي نتفادى جمع الأعداد الفردية (لاحظ أنّ قيمة i بدأت من الصفر). فيما عدا ذلك يبدو هذا البرنامج مطابقًا لبنية البرنامج Lesson04_1. حلقة while التكرارية لهذه الحلقة التكراريّة الشكل العام التالي: while (loop_condition) { statement1; statement2; ... } ستتكرّر العبارات البرمجيّة الموجودة ضمن حاضنة while طالما كان الشرط loop_condition محقّقًا (أي true) وبمجرّد أن يصبح الشرط loop_condition غير محقّق تتوقّف الحلقة عن التكرار. سنعدّل البرنامج Lesson04_1 السابق لكي يسمح باستخدام الحلقة while. أنشئ مشروعًا جديدًا وسمّه Lesson04_3 ثم استبدل محتويات Program.cs بما يلي: 1 using System; 2 3 namespace Lesson04_3 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 int n, sum = 0, i = 1; 10 string str_n; 11 12 Console.WriteLine("This Program calculates the series: sum = 1 + 2 + 3 + ... + n"); 13 Console.Write("Input 'n' please: "); 14 str_n = Console.ReadLine(); 15 16 n = int.Parse(str_n); 17 18 while (i <= n) 19 { 20 sum += i; 21 22 i++; 23 } 24 25 Console.WriteLine("sum = {0}", sum); 26 } 27 } 28 } صرّحنا عن المتغيّر i وأسندنا له القيمة 1 في السطر 9 والذي سيمثّل متغيّر حلقة while. استبدلنا حلقة for بحلقة while في السطر 18 مع ملاحظة أنّ شرط استمرار الحلقة i <= 5 بقي دون تعديل. لاحظ العبارة المهمّة في السطر 22 والتي تحوي التعبير ++i الذي سيزيد قيمة i بمقدار واحد في كلّ دورة. إنّ إغفال هذه العبارة سيؤدّي إلى الدخول في حلقة لا نهائيّة، لأنّ شرط الاستمرار في هذه الحالة لن يعطي false أبدًا لأنّ قيمة i لن تتغيّر. نفّذ البرنامج وأدخل قيم مختلفة للمتغيّر n لاختبار البرنامج. جرّب الآن إدخال القيمة 0 للمتغيّر n ستحصل في الخرج على المجموع sum = 0 وهذا منطقيّ. إذ أنّنا نخبر البرنامج بأنّنا لا نريد جمع أي عدد. سبب الحصول على هذا الخرج في الواقع هو أنّ البرنامج أثناء التنفيذ لن يدخل إلى حلقة while مطلقًا لأنّ شرط الاستمرار i <= n سيكون غير محقّقًا منذ البداية (تذكّر أنّ قيمة i الابتدائيّة هي 1). حلقة do-while التكرارية لهذه الحلقة الشكل العام التالي: do { statement1; statement2; ... } while (loop_condition) وهي تشبه الحلقة while باستثناء أنّ شرط استمرار الحلقة loop_condition يجري اختباره في نهايتها وليس في بدايتها كما هو الحال مع حلقة while. قد لا يبدو هذا الأمر مهمًّا في البداية ولكنّه في الحقيقة عكس ذلك تمامًا. لفهم الفرق أنشئ مشروعًا جديدًا وسمّه Lesson04_04 ثمّ استبدل الشيفرة الموجودة في Program.cs بالشيفرة التالية: 1 using System; 2 3 namespace Lesson04_4 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 int n, sum = 0, i = 1; 10 string str_n; 11 12 Console.WriteLine("This Program calculates the series: sum = 1 + 2 + 3 + ... + n"); 13 Console.Write("Input 'n' please: "); 14 str_n = Console.ReadLine(); 15 16 n = int.Parse(str_n); 17 18 Do 19 { 20 sum += i; 21 22 i++; 23 } 24 while (i <= n); 25 26 Console.WriteLine("sum = {0}", sum); 27 } 28 } 29 } لا يختلف هذا البرنامج عن سابقيه في حساب مجموع السلسلة 1 + 2 + 3 + … + n، نفّذ البرنامج وأدخل القيمة 0 للمتغيّر n ستحصل في الخرج على المجموع sum = 1 وهذا خطأ بالطبع! السبب في ذلك أنّ اختبار شرط الاستمرار في حلقة do-while يجري بعد انتهاء الحلقة من تنفيذ أوّل دورة لها، حيث تؤدّي هذه الدورة إلى جعل قيمة المتغيّر sum تساوي 1 وقيمة i تساوي 2، وبعد ذلك يأتي اختبار الشرط i <= n والذي سيعطي false بالطبع وتتوقف الحلقة عن التكرار ولكن بعد فوات الأوان. في حلقة while (وحتى في حلقة for) لم نواجه هذه المشكلة لأنّ شرط استمرارها يجري اختباره في بداية الحلقة وقبل تنفيذ أي دورة تكراريّة. تمارين داعمة تمرين 1 اكتب برنامجًا يطبع الأعداد من 1 حتى 100 على الشاشة باستثناء الأعداد من مضاعفات العدد 5 أي على الشكل التالي: 1, 2, 3, 4, 6, 7, 8, 9, 11, … , 14, 16, … تلميح: ستحتاج في هذا التمرين إلى استخدام بنية if ضمن حلقة for واختبار قيمة التعبير المنطقي i % 5 == 0 على افتراض أنّ i هو عدّاد الحلقة. تمرين 2 اكتب برنامجًا يطلب من المستخدم إدخال عدد صحيح موجب ثمّ يوجد مضروب هذا عدد (قيمة العاملي له). فإذا أدخل عددًا سالبًا يجب على البرنامج أن ينبّه المستخدم على ذلك ويُنهي التنفيذ. تلميح: تذكّر أنّ مضروب عدد يُعبّر عن الجداءات للقيم المتناقصة لهذا العدد فمثلًا لإيجاد مضروب 5 (!5) نكتب: 5! = 5 * 4 * 3 * 2 * 1 تذكّر أيضًا أنّ !1 =1 و !0 = 1. الخلاصة تحدثنا في هذا الدرس عن الحلقات التكراريّة بأنواعها المختلفة. تدعم سي شارب عدة حلقات تكراريّة تُعتبر حلقة for من أهمّها. في الحقيقة توجد حلقة تكراريّة أخرى لم نتحدّث عنها في هذا الدرس، وهي حلقة foreach، وهي حلقة مفيدة جدًّا أجلّت الحديث عنها إلى أن نتعرّف على المجموعات Collections بأنواعها ونتعلّم التعامل معها.
  6. تعتبر العبارات الشرطية في البرنامج من الأمور الأساسيّة في البرمجة كما هو معلوم. تمتلك لغة سي شارب نوعين من العبارات الشرطية وهما: بنية if-else وبنية switch-case. العبارة الشرطية if-else وهي بنية مألوفة في معظم لغات البرمجة، تشبه هذه البنية في تشكيلها تلك الموجودة في لغات أخرى مثل ++C و Java. تمتلك هذه البنية ثلاثة أشكال سنتحدّث عنها تباعًا. الشكل الأول لبنية if الشكل الأبسط لبنية if هي: if ([condition]) { statement1; statement2; ... } إذا كان تقييم evaluate الشرط [condition] يعطينا true (أي تحقّق الشرط) عندها ستُفّذ العبارات البرمجيّة الموجودة ضمن الحاضنة {}، وإلّا (أي لم يتحقّق الشرط) فلن يُنفّذ أيّ منها. أنشئ مشروعًا جديدًا سمّه Lesson03_1 واستبدل محتويات الملف Program.cs بالبرنامج البسيط التالي الذي يعمل على مقارنة القيمة المدخلة من المستخدم مع العدد 5 ويُظهر الخرج المناسب: 1 using System; 2 3 namespace Lesson03_1 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 double x; 10 string str_x; 11 12 Console.Write("Input a number: "); 13 str_x = Console.ReadLine(); 14 15 x = double.Parse(str_x); 16 17 if(x > 5) 18 { 19 Console.WriteLine("The value {0} is greater than 5", x); 20 } 21 22 Console.WriteLine("Goodbye!"); 23 } 24 } 25 } جرّب تنفيذ هذا البرنامج باستخدام Ctrl+F5 (أو من القائمة Debug > Start Without Debugging). سيطلب منك البرنامج إدخال قيمة عدديّة، أدخل العدد 6، سيعرض البرنامج الخرج التالي: The value 6 is greater than 5 Goodbye! أعد تنفيذ البرنامج وأدخل هذه المرّة القيمة 3 لتحصل على الخرج التالي: Goodbye! لاحظ بأنّ خرج البرنامج قد اختلف باختلاف القيم المدخلة، أي أنّ هناك اختلاف في العبارات البرمجيّة التي تمّ تنفيذها في كلّ مرّة. يعود سبب ذلك إلى البنية if الموجودة بين السطرين 17 و 20. يختبر الشرط الموجود بعد كلمة if في السطر 17 فيما إذا كانت قيمة المتغيّر x أكبر تمامًا من 5. فإذا كانت نتيجة تقييم التعبير x > 5 تساوي true فهذا يعني أنّ الشرط قد تحقّق وبالتالي تنفّذ جميع العبارات البرمجيّة الموجودة في الحاضنة (بين السطرين 18 و 20). أمّا إذا كانت نتيجة تقييم التعبير x > 5 تساوي false فعندها سيتجاوز تنفيذ البرنامج البنية if إلى العبارات التي تأتي بعد السطر 20. الشكل الثاني لبنية if هذا الشكل للعبارة الشرطية if مفيد أيضًا، ويُستخدم عندما نريد الاختيار بين مجموعتين من العبارات البرمجيّة، والشكل العام له: if ([condition]) { statement1; statement2; ... } else { Statement3; Statement4; ... } لقد أضفنا القسم else مع حاضنته. المنطق هنا بسيط يمكننا قراءته بالشكل التالي: "إذا تحقق الشرط [condition] عندها تنفّذ الحاضنة الموجودة بعد if مباشرةً، وإلّا يتم تنفيذ الحاضنة الموجودة بعد else مباشرةً" لكي نتعرّف على كيفيّة التعامل مع هذا الشكل، أنشئ مشروعًا جديدًا سمّه Lesson03_2 وانسخ الشيفرة التالية إلى الملف Program.cs: 1 using System; 2 3 namespace Lesson03_2 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 double x; 10 string str_x; 11 12 Console.Write("Input a number: "); 13 str_x = Console.ReadLine(); 14 15 x = double.Parse(str_x); 16 17 if (x > 5) 18 { 19 Console.WriteLine("The value {0} is greater than 5", x); 20 } 21 else 22 { 23 Console.WriteLine("The value {0} is smaller than or equals 5", x); 24 } 25 26 Console.WriteLine("Goodbye!"); 27 } 28 } 29 } هذا البرنامج مطابق للبرنامج الذي رأيناه قبل قليل باستثناء القسم else مع حاضنته. يسلك هذا البرنامج نفس السلوك الذي يسلكه البرنامج السابق باستثناء أنّه لو أدخل المستخدم قيمة مثل 3 سيعمل البرنامج على طباعة الخرج التالي: The value 3 is smaller than or equals 5 Goodbye! لاحظ أنّ البرنامج Lesson03_1 كان يطبع العبارة !Goodbye فقط عند إدخال القيمة 3. السبب في ظهور الخرج الجديد هو وجود القسم else في بنية if السابقة، فعندما يُقيّم الشرط x > 5 في السطر 17 وتكون نتيجة تقييمه false سينتقل البرنامج فورًا إلى تنفيذ العبارات البرمجيّة الموجودة ضمن حاضنة else وهذا هو سبب ظهور هذا الخرج. العيب الوحيد في هذا البرنامج أنّه لا يستطيع التمييز بين الحالة التي تكون فيها القيمة المدخلة تساوي 5 وبين الحالة التي تكون فيها أصغر تمامًا من 5، ففي كلّ من هاتين الحالتين يعرض البرنامج نفس الخرج عن طريق تنفيذ العبارة الموجودة في السطر 23. ملاحظة: في حال كانت أيّة حاضنة تحوي عبارة برمجيّة واحد فقط، فعندها يمكن عدم استخدام قوسي الحاضنة {} مع أنّني أفضّل استخدامهما لجعل البرنامج أكثر وضوحًا. الشكل الثالث لبنية if وهو الشكل الأكثر شمولًا وفيه نستخدم القسم else if على الصورة التالية: if ([condition]) { statement1; statement2; ... } else if ([condition1]) { Statement3; Statement4; ... } else if ([condition2]) { Statement3; Statement4; ... } ... else { Statement3; Statement4; ... } يمكننا قراءة المنطق هنا على الشكل التالي: "إذا تحقّق الشرط [condition] عندها تنفّذ الحاضنة الموجودة بعد if مباشرةً، وإلّا إذا (else if) تحقّق الشرط [condition1] يتم تنفيذ الحاضنة الموجودة بعد else if الأولى مباشرةً، وإلّا إذا تحقّق الشرط [condition2] يتم تنفيذ الحاضنة الموجودة بعد else if الثانية مباشرةً، وإلّا (else) يتم تنفيذ الحاضنة الموجودة بعد else مباشرةً" نلاحظ أنّه يمكننا استخدام أقسام else if بقدر ما نريد، ولكن يمكن استخدام قسم else وحيد. ونلاحظ أيضًا أنّه بالنتيجة ستنفّذ مجموعة واحدة فقط ضمن حاضنة ما. وواضح أيضًا أنّ أقسام else if و else هي أقسام اختياريّة ووجودها غير مرتبط ببعضها، ولكن إذا حوت بنية if قسم else if فيجب أي يكون القسم else (في حال وجوده) هو القسم الأخير. لكي نثبّت هذا المفهوم بشكل جيّد انظر البرنامج Lesson03_3 التالي: 1 using System; 2 3 namespace Lesson03_3 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 double x; 10 string str_x; 11 12 Console.Write("Input a number: "); 13 str_x = Console.ReadLine(); 14 15 x = double.Parse(str_x); 16 17 if (x > 5) 18 { 19 Console.WriteLine("The value {0} is greater than 5", x); 20 } 21 else if (x == 5) 22 { 23 Console.WriteLine("The value {0} is equals 5", x); 24 } 25 else 26 { 27 Console.WriteLine("The value {0} is smaller than 5", x); 28 } 29 30 Console.WriteLine("Goodbye!"); 31 } 32 } 33 } يشبه هذا البرنامج سابقيه إلى حدٍّ بعيد، فهو يقارن القيمة المدخلة مع العدد 5 ويعرض رسالة مناسبة نتيجة عملية المقارنة. الشيء الجديد هنا هو التمييز بين الحالة التي تكون فيها القيمة المدخلة تساوي العدد 5 والحالة التي تكون فيها أصغر من العدد 5. قمنا بذلك من خلال إضافة القسم else if جديد يختبر حالة المساواة مع العدد 5. الآن أصبح منطق البرنامج كالتالي: "إذا كانت القيمة المدخلة أكبر تمامًا من 5 (السطر 17) عندها تُنفّذ العبارة الموجودة في السطر 19، وإلّا إذا كانت القيمة المدخلة تساوي 5 (السطر 21) عندها تُنفّذ العبارة الموجودة في السطر 23، وإلّا ستكون القيمة المدخلة أصغر من 5 حتمًا، وتُنفَّذ العبارة الموجودة في السطر 27." العبارة الشرطية switch-case تفيد هذه البنية في الاختيار من بين عدّة حالات منفصلة. لهذه البنية الشكل العام التالي: switch(expression) { case [A]: [statements] break; case [B]: [statements] break; ... [default:] [statements] break; } القسم الأخير default هو قسم اختياري، كما يجب أن يكون هناك قسم case واحد على الأقل. إليك الآن البرنامج Lesson03_4 لفهم كيفيّة استخدام هذه البنية: 1 using System; 2 3 namespace Lesson03_4 4 { 5 class Program 6 { 7 static void Main(string[] args) 8 { 9 double x, y; 10 string str_x, str_y, operation; 11 12 Console.Write("Input first number: "); 13 str_x = Console.ReadLine(); 14 15 Console.Write("Input second number: "); 16 str_y = Console.ReadLine(); 17 18 Console.Write("Choose operation (+, -, *, /): "); 19 operation = Console.ReadLine(); 20 21 x = double.Parse(str_x); 22 y = double.Parse(str_y); 23 24 switch (operation) 25 { 26 case "+": 27 Console.WriteLine("{0} + {1} = {2}", x, y, x + y); 28 break; 29 case "-": 30 Console.WriteLine("{0} - {1} = {2}", x, y, x - y); 31 break; 32 case "*": 33 Console.WriteLine("{0} * {1} = {2}", x, y, x * y); 34 break; 35 case "/": 36 Console.WriteLine("{0} / {1} = {2}", x, y, x / y); 37 break; 38 default: 39 Console.WriteLine("Unsupported operation."); 40 break; 41 } 42 } 43 } 44 } البرنامج السابق عبارة عن برنامج آلة حاسبة بسيطة تدعم العمليات الحسابية الأربع: الجمع والطرح والضرب والقسمة. يطلب البرنامج من المستخدم إدخال قيمتين عدديّتين، بعد ذلك يطلب اختيار العمليّة الحسابيّة المراد إجراؤها على هاتين القيمتين (+ ، - ، * ، /) وتخزين العمليّة المختارة ضمن المتغيّر النصّي operation وذلك في السطر 19. تعمل البنية switch في السطر 24 على مقارنة قيمة المتغيّر النصيّ operation مع القيم الموجودة في أقسام case (الأسطر 26 و 29 و 32 و 35) فإذا طابقت القيمة الموجودة في operation إحدى تلك القيم، فإنّ العبارات البرمجيّة الموجودة ضمن هذا القسم سيتمّ تنفيذها. أمّا إذا لم يحدث مثل هذا التطابق، فستنفّذ العبارات البرمجيّة الموجودة في القسم الاختياري default والتي ستخبر المستخدم (في هذا المثال) بأنّ العمليّة الحسابيّة التي يرغبها لا يدعمها البرنامج. نستفيد من القسم default في تنفيذ عبارات برمجيّة في حال لم يحدث التطابق مع أيّ قسم case سابق. كما نلاحظ أنّ العبارة break الموجودة في كلّ قسم من أقسام case بالإضافة إلى قسم default هي عبارة ضرورية وتؤدّي إلى انتقال تنفيذ البرنامج إلى خارج بنية switch أي إلى السطر 42. جرّب تنفيذ البرنامج وإدخال قيم متنوّعة بالإضافة إلى تجريب العمليات الحسابيّة الأربع. جرّب إدخال عامل باقي القسمة مثلًا (%) وانظر كيف سيجيب البرنامج بالرسالة Unsupported operation. تمارين داعمة تمرين 1 في البرنامج Lesson03_4 السابق إذا أدخل المستخدم القيمة 0 للعدد الثاني، ثم اختار عمليّة القسمة ( / ) سيؤدّي ذلك إلى القسمة على صفر، وهذا يسبّب خطًأ أثناء التنفيذ runtime error يؤدّي إلى رمي استثناء وتوقّف البرنامج عن العمل. أجرِ تعديلًا على البرنامج ليأخذ هذا الأمر بالحسبان. (تلميح: أضف شرط if ضمن قسم case الموافق للعمليّة ( / ) لاختبار قيمة المتغيّر y فيما إذا كانت تساوي الصفر أم لا). تمرين 2 اكتب برنامجًا يطلب من المستخدم إدخال درجة الحرارة الحاليّة. فإذا كانت درجة الحرارة أقل من 4 مئوية يعرض البرنامج الرسالة "Very Cold". أمّا إذا كانت درجة الحرارة بين 4 وأقل من 10 مئويّة يعرض الرسالة "Cold". وفي حال كانت درجة الحرارة بين 10 وأقل من 30 مئويّة يعرض الرسالة "Normal". أمّا إذا كانت درجة الحرارة 30 فما فوق فيعرض البرنامج الرسالة "Hot". الخلاصة تعلّمنا في هذا الدرس مبادئ التعامل مع العبارات الشرطية والحاجة الماسّة إليها في اتخاذ القرارات المناسبة في البرنامج. تعرّفنا على العبارة الشرطية if-else وأشكالها المفيدة، كما تعرّفنا أيضًا على بنية الاختيار swicth-case. في مجال البرمجة من غير الممكن في الواقع أن يخلو أيّ برنامج فعليّ من وجود عبارة شرطية if واحدة على الأقل.