Skip to content

3. أساسيات لغة C#

3.1. مقدمة

سنبدأ بالتعامل مع C# كلغة برمجة كلاسيكية. وسنتناول الفئات لاحقًا. هناك عنصران في البرنامج:

  • البيانات
  • التعليمات التي تتعامل معها

نحاول عمومًا فصل البيانات عن التعليمات:

3.2. بيانات C#

تستخدم لغة C# أنواع البيانات التالية:

  1. الأعداد الصحيحة
  2. الأعداد الحقيقية
  3. الأعداد العشرية
  4. الأحرف والسلاسل
  5. القيم المنطقية
  6. الكائنات

3.2.1. أنواع البيانات المحددة مسبقًا

نوع C#
نوع .NET
البيانات الممثلة
اللاحقة
القيم الحرفية
الترميز
مجال القيمة
حرف
حرف (حرف)
حرف
 
2 بايت
حرف يونيكود (UTF-16)
سلسلة
سلسلة (C)
سلسلة أحرف
  
إشارة إلى تسلسل أحرف Unicode
int
Int32 (S)
عدد صحيح
 
4 بايت
[-231, 231-1] [-2147483648, 2147483647]
uint
UInt32 (S)
..
U
4 بايت
[0، 232-1] [0، 4294967295]
long
Int64 (S)
..
L
8 بايت
[-263, 263 -1] [-9223372036854775808, 9223372036854775807]
ulong
UInt64 (S)
..
UL
8 بايت
[0، 264 -1] [0، 18446744073709551615]
sbyte
 
..
 
1 بايت
[-27 ، 27 -1] [-128،+127]
بايت
بايت (S)
..
 
1 بايت
[0 , 28 -1] [0,255]
قصير
Int16 (S)
..
 
2 بايت
[-215, 215-1] [-32768, 32767]
ushort
UInt16 (S)
..
 
2 بايت
[0، 216-1] [0،65535]
عائم
عدد حقيقي أحادي (S)
عدد حقيقي
F
4 بايت
[1.5 × 10⁻⁴⁵، 3.4 × 10³⁸ بالقيم المطلقة
مزدوج
مزدوج (S)
..
D
8 بايت
[-1.7 × 10³⁰⁸، 1.7 × 10³⁰⁸ بالقيم المطلقة
عشري
عشري (S)
رقم عشري
M
16 بايت
[1.0 10⁻²⁸, 7.9 10⁺²⁸] بالقيمة المطلقة مع 28 رقمًا معنويًا
bool
منطقية (S)
..
 
1 بايت
صحيح، خطأ
كائن
كائن (C)
مرجع الكائن
  
مرجع الكائن

فيما سبق، تمت الإشارة إلى أنواع C# بنوع .NET المكافئ لها، مع التعليق (S) إذا كان النوع عبارة عن بنية و(C) إذا كان النوع عبارة عن فئة. وقد تبين أن هناك نوعين محتملين لعدد صحيح 32 بت: int و Int32. النوع int هو نوع C#. Int32 هي بنية تنتمي إلى System. اسمها الكامل هو System.Int32. النوع int هو اسم مستعار في C# للبنية .NET System.Int32. وبالمثل، فإن سلسلة C# هي اسم مستعار للنوع .NET System.String. System.String هي فئة وليست بنية. المفهومان متشابهان، ولكن مع الاختلاف الأساسي التالي:

  • يمكن معالجة متغير من النوع Structure عبر قيمته
  • يمكن معالجة متغير من النوع Class عبر عنوانه (مرجع في لغة الكائنات).

البنية كفئة هي أنواع معقدة ذات سمات وأساليب. على سبيل المثال، يمكننا كتابة:

string nomDuType=3.GetType().FullName;

ما ورد أعلاه هو القيمة الثابتة 3 التي تعتبر في C# بشكل افتراضي من النوع int، من نوع .NET System.Int32. تحتوي هذه البنية على دالة GetType() التي تعرض كائنًا يغلف خصائص نوع البيانات System.Int32. وتشمل هذه الخصائص الخاصية FullName التي تُرجع الاسم الكامل للنوع. كما ترى، فإن القيمة الثابتة 3 أكثر تعقيدًا مما تبدو عليه في البداية.

فيما يلي برنامج يوضح هذه النقاط:


using System;
 
namespace Chap1 {
    class P00 {
        static void Main(string[] args) {
             // example 1
            int ent = 2;
            float fl = 10.5F;
            double d = -4.6;
            string s = "essai";
            uint ui = 5;
            long l = 1000;
            ulong ul = 1001;
            byte octet = 5;
            short sh = -4;
            ushort ush = 10;
            decimal dec = 10.67M;
            bool b = true;
            Console.WriteLine("Type de ent[{1}] : [{0},{2}]", ent.GetType().FullName, ent,sizeof(int));
            Console.WriteLine("Type de fl[{1}]: [{0},{2}]", fl.GetType().FullName, fl, sizeof(float));
            Console.WriteLine("Type de d[{1}] : [{0},{2}]", d.GetType().FullName, d, sizeof(double));
            Console.WriteLine("Type de s[{1}] : [{0}]", s.GetType().FullName, s);
            Console.WriteLine("Type de ui[{1}] : [{0},{2}]", ui.GetType().FullName, ui, sizeof(uint));
            Console.WriteLine("Type de l[{1}] : [{0},{2}]", l.GetType().FullName, l, sizeof(long));
            Console.WriteLine("Type de ul[{1}] : [{0},{2}]", ul.GetType().FullName, ul, sizeof(ulong));
            Console.WriteLine("Type de b[{1}] : [{0},{2}]", octet.GetType().FullName, octet, sizeof(byte));
            Console.WriteLine("Type de sh[{1}] : [{0},{2}]", sh.GetType().FullName, sh, sizeof(short));
            Console.WriteLine("Type de ush[{1}] : [{0},{2}]", ush.GetType().FullName, ush, sizeof(ushort));
            Console.WriteLine("Type de dec[{1}] : [{0},{2}]", dec.GetType().FullName, dec, sizeof(decimal));
            Console.WriteLine("Type de b[{1}] : [{0},{2}]", b.GetType().FullName, b, sizeof(bool));
        }
    }
}
  • السطر 7: إعلان متغير عدد صحيح
  • السطر 19: يمكن الحصول على نوع المتغير v بواسطة v.GetType().FullName. يمكن الحصول على حجم بنية S بواسطة sizeof(S). تعليمة Console.WriteLine("... {0} ... {1} ...", param0, param1, ...) تكتب على الشاشة النص الذي يمثل المعلمة الأولى، مع استبدال كل رمز {i} بقيمة التعبير parami.
  • السطر 22: لا يمكن استخدام عامل النوع string، لأنه يشير إلى فئة وليس إلى بنية sizeof.

إليك النتيجة:

Type de ent[2] : [System.Int32,4]
Type de fl[10,5]: [System.Single,4]
Type de d[-4,6] : [System.Double,8]
Type de s[essai] : [System.String]
Type de ui[5] : [System.UInt32,4]
Type de l[1000] : [System.Int64,8]
Type de ul[1001] : [System.UInt64,8]
Type de b[5] : [System.Byte,1]
Type de sh[-4] : [System.Int16,2]
Type de ush[10] : [System.UInt16,2]
Type de dec[10,67] : [System.Decimal,16]
Type de b[True] : [System.Boolean,1]

تعرض الشاشة أنواع .NET، وليس أسماء مستعارة لـ C#.

3.2.2. ترميز البيانات الحرفية

عدد صحيح int (32 بت)
145، -7، 0xFF (سداسي عشري)
عدد صحيح طويل (64 بت) - لاحقة L
100000L
عدد حقيقي مزدوج
134.789، -45E-18 (-45 × 10⁻¹⁸)
عدد حقيقي عائم (اللاحقة F)
134.789F، -45E-18F (-45 × 10⁻¹⁸)
عدد حقيقي عشري (اللاحقة M)
100000M
حرف char
'A'، 'b'
سلسلة أحرف string
"اليوم" "c:\chap1\paragraph3" @"c:\chap1\paragraph3"
قيمة منطقية bool
صحيح، خطأ
تاريخ
new DateTime(1954,10,13) (السنة، الشهر، اليوم) لـ 13/10/1954

لاحظ السلسلتين الحرفيتين: "c:\chap1\\paragraph3" و @"c:\chap1\paragraph3". في السلاسل الحرفية، يتم تفسير الحرف \. وبالتالي، فإن "\n" يمثل علامة نهاية السطر وليس تسلسل الحرفين \ و n. وإذا أردنا هذا التسلسل، فسيتعين علينا كتابة "\\n" حيث يُفسر التسلسل على أنه حرف \ واحد. يمكنك أيضًا كتابة @"\n" للحصول على نفس النتيجة. تتطلب صيغة @"text" أن يؤخذ النص بالضبط كما هو مكتوب. ويُطلق على هذا أحيانًا اسم السلسلة الحرفية.

3.2.3. إعداد تقارير البيانات

3.2.3.1. دور الإعلانات

يقوم البرنامج بمعالجة البيانات التي تتميز باسم ونوع. يتم تخزين هذه البيانات في الذاكرة. عند ترجمة البرنامج، يقوم المُترجم بتعيين موقع ذاكرة لكل عنصر من عناصر البيانات يتميز بعنوان وحجم. ويقوم بذلك بمساعدة الإعلانات التي يقوم بها المبرمج.

كما أنها تمكّن المُترجم من اكتشاف أخطاء البرمجة. على سبيل المثال،

x=x*2;

سيتم إعلانها على أنها خاطئة إذا كان x سلسلة، على سبيل المثال.

3.2.3.2. إعلان الثوابت

صيغة إعلان الثابت هي كما يلي:

    const type nom=valeur;          //définit constante nom=valeur

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

const float myPI=3.141592F;    

لماذا نعلن الثوابت؟

  1. سيكون البرنامج أسهل في القراءة إذا أُعطيت الثابتة اسمًا ذي معنى:
    const  float taux_tva=0.186F;
  1. سيكون تعديل البرنامج أسهل إذا تغيرت "الثابتة". على سبيل المثال، في الحالة السابقة، إذا تغير معدل ضريبة القيمة المضافة إلى 33٪، فإن التعديل الوحيد الذي يجب إجراؤه هو تعديل التعليمات التي تحدد قيمتها:
    const float taux_tva=0.33F;

إذا تم استخدام 0.186 بشكل صريح في البرنامج، فسيتعين تعديل العديد من التعليمات.

3.2.3.3. إعلان المتغير

يتم تحديد المتغير بواسطة اسم ويشير إلى نوع البيانات. لغة C# حساسة لحالة الأحرف. وبالتالي فإن FIN و end مختلفان.

يمكن تهيئة المتغيرات عند إعلانها. صيغة إعلان متغير واحد أو أكثر هي:

 Identificateur_de_type variable1[=valeur1],variable2=[valeur2],...;

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

يمكنك أيضًا تجنب تحديد النوع الدقيق للمتغير باستخدام الكلمة الرئيسية var بدلاً من Identificateur_de_type :

 var variable1=valeur1,variable2=valeur2,...;

الكلمة الرئيسية var لا تعني أن المتغيرات ليس لها نوع محدد. المتغير variablei له نوع البيانات valuei المخصص له. التهيئة هنا إلزامية حتى يتمكن المُترجم من استنتاج نوع المتغير.

إليك مثال:


using System;
 
namespace Chap1 {
    class P00 {
        static void Main(string[] args) {
            int i=2;
            Console.WriteLine("Type de int i=2 : {0},{1}",i.GetType().Name,i.GetType().FullName);
            var j = 3;
            Console.WriteLine("Type de var j=3 : {0},{1}", j.GetType().Name, j.GetType().FullName);
            var aujourdhui = DateTime.Now;
            Console.WriteLine("Type de var aujourdhui : {0},{1}", aujourdhui.GetType().Name, aujourdhui.GetType().FullName);
        }
    }
}
  • السطر 6: بيانات مكتوبة بشكل صريح
  • السطر 7: (data).GetType().Name هو الاسم المختصر لـ (data)، و(data).GetType().FullName هو الاسم الكامل لـ (data)
  • السطر 8: بيانات مكتوبة ضمناً. لأن 3 من النوع int، فإن j سيكون من النوع int.
  • السطر 10: بيانات مكتوبة ضمناً. لأن نوع DateTime.Now هو DateTime، فإن نوع today سيكون DateTime.

عند التنفيذ، نحصل على النتيجة التالية:

1
2
3
Type de int i=2 : Int32,System.Int32
Type de var j=3 : Int32,System.Int32
Type de var aujourdhui : DateTime,System.DateTime

لا يمكن تغيير نوع المتغير الذي تم تعريفه ضمناً باستخدام الكلمة الرئيسية var. على سبيل المثال، بعد السطر 10 من الكود، السطر:


            var aujourdhui = "aujourd'hui";

سنرى لاحقًا أنه من الممكن إعلان نوع "أثناء التشغيل" في تعبير. في هذه الحالة، يكون نوعًا مجهولًا، وهو نوع لم يطلق عليه المستخدم اسمًا. سيقوم المُترجم بإعطاء هذا النوع الجديد اسمًا. إذا كان من المقرر تعيين نوع مجهول لمتغير، فإن الطريقة الوحيدة لإعلانه هي استخدام الكلمة الرئيسية var.

3.2.4. التحويلات بين الأرقام والسلاسل

رقم -> سلسلة
nombre.ToString()
سلسلة -> int
int.Parse(string) أو System.Int32.Parse
سلسلة -> طويل
long.Parse(string) أو System.Int64.Parse
سلسلة -> double
double.Parse(string) أو System.Double.Parse(string)
سلسلة -> float
float.Parse(string) أو System.Float.Parse(string)

قد يفشل تحويل سلسلة إلى رقم إذا كانت السلسلة لا تمثل رقمًا صالحًا. خطأ فادح يُسمى استثناء. يمكن معالجة هذا الخطأ باستخدام try/catch التالي:


try{
    appel de la fonction susceptible de générer l'exception
} catch (Exception e){
    traiter l'exception e
}
instruction suivante

إذا لم تُحدث الدالة استثناءً، ننتقل إلى التعليمات التالية، وإلا فإننا ننتقل إلى نص جملة catch ثم إلى التعليمات التالية. سنعود إلى معالجة الاستثناءات لاحقًا. إليك برنامج يوضح بعض التقنيات للتحويل بين الأرقام والسلاسل النصية. في هذا المثال، تقوم الدالة poster بكتابة قيمة معلمتها على الشاشة. وبالتالي، فإن poster(S) تكتب قيمة S على الشاشة، حيث يكون S من النوع string.


using System;
 
namespace Chap1 {
    class P01 {
        static void Main(string[] args) {
 
             // data
            const int i = 10;
            const long l = 100000;
            const float f = 45.78F;
            double d = -14.98;
 
             // number --> string
            affiche(i.ToString());
            affiche(l.ToString());
            affiche(f.ToString());
            affiche(d.ToString());
 
             //boolean --> string
            const bool b = false;
            affiche(b.ToString());
 
             // string --> int
            int i1;
            i1 = int.Parse("10");
            affiche(i1.ToString());
            try {
                i1 = int.Parse("10.67");
                affiche(i1.ToString());
            } catch (Exception e) {
                affiche("Erreur : " + e.Message);
            }
 
             // chain --> long
            long l1;
            l1 = long.Parse("100");
            affiche(l1.ToString());
            try {
                l1 = long.Parse("10.675");
                affiche(l1.ToString());
            } catch (Exception e) {
                affiche("Erreur : " + e.Message);
            }
 
             // chain --> double
            double d1;
            d1 = double.Parse("100,87");
            affiche(d1.ToString());
            try {
                d1 = double.Parse("abcd");
                affiche(d1.ToString());
            } catch (Exception e) {
                affiche("Erreur : " + e.Message);
            }
 
             // string --> float
            float f1;
            f1 = float.Parse("100,87");
            affiche(f1.ToString());
            try {
                d1 = float.Parse("abcd");
                affiche(f1.ToString());
            } catch (Exception e) {
                affiche("Erreur : " + e.Message);
            }
 
         }// fine hand
 
        public static void affiche(string S) {
            Console.Out.WriteLine("S={0}",S);
        }
     }// end of class
}

تتعامل الأسطر 30-32 مع الاستثناء المحتمل الذي قد يحدث. e.Message هي رسالة الخطأ المرتبطة بالاستثناء e.

والنتائج هي كما يلي:

S=10
S=100000
S=45,78
S=-14,98
S=False
S=10
S=Erreur : Input string was not in a correct format.
S=100
S=Erreur : Input string was not in a correct format.
S=100,87
S=Erreur : Input string was not in a correct format.
S=100,87
S=Erreur : Input string was not in a correct format.

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

double d1=10.7; 

ولكن

double d2=int.Parse("10,7");

3.2.5. جداول البيانات

مصفوفة C# هي كائن يُستخدم لتجميع البيانات من نفس النوع تحت نفس المعرف. ويتم تعريفها على النحو التالي:

Type[] tableau[]=new Type[n]

n هو عدد عناصر البيانات التي يمكن أن يحتوي عليها المصفوف. تحدد صيغة Table[i] البيانات رقم i حيث ينتمي i إلى الفاصل [0,n-1]. أي إشارة إلى البيانات Table[i] حيث لا ينتمي i إلى الفاصل [0,n-1] ستؤدي إلى حدوث استثناء. يمكن تهيئة المصفوف وكذلك إعلانه:

    int[] entiers=new int[] {0,10,20,30};

أو ببساطة:

    int[] entiers={0,10,20,30};

تحتوي المصفوفات على خاصية Length وهي عدد العناصر الموجودة في المصفوفة.

يمكن تعريف صورة ثنائية الأبعاد على النحو التالي:

Type[,] array=new Type[n,m];

حيث n هو عدد الصفوف، و m هو عدد الأعمدة. تشير صيغة Table[i,j] إلى العنصر j في الصف i من الجدول. يمكن أيضًا تهيئة المصفوفة ثنائية الأبعاد في نفس وقت تعريفها:

    double[,] réels=new double[,] { {0.5, 1.7}, {8.4, -6}};

أو ببساطة:

    double[,] réels={ {0.5, 1.7}, {8.4, -6}};

يمكن الحصول على عدد العناصر في كل بُعد باستخدام GetLength(i) حيث يمثل i=0 البُعد المقابل للمؤشر الأول، و i=1 البُعد المقابل للمؤشر الثاني، ...

يتم الحصول على العدد الإجمالي للأبعاد باستخدام الخاصية Rank، والعدد الإجمالي للعناصر باستخدام Length.

يتم إعلان جدول الجداول على النحو التالي:

Type[][] array=new Type[n][];

يُنشئ البيان أعلاه مصفوفة من n صفوف. كل عنصر table[i] هو مرجع مصفوفة أحادي البعد. لا يتم تهيئة هذه المراجع array[i] في الإعلان أعلاه. قيمتها هي المرجع null.

يوضح المثال التالي إنشاء مصفوفة من الجداول:


            // un tableau de tableaux
            string[][] noms = new string[3][];
            for (int i = 0; i < noms.Length; i++) {
                noms[i] = new string[i + 1];
            }//for
            // initialisation
            for (int i = 0; i < noms.Length; i++) {
                for (int j = 0; j < noms[i].Length; j++) {
                    noms[i][j] = "nom" + i + j;
                }//for j
            }//for i
  • السطر 2: جدول «names» يتكون من 3 مصفوفات من نوع string[][]. كل عنصر هو مؤشر إلى مصفوفة (إشارة إلى كائن) تتكون عناصرها من نوع string[].
  • السطور 3-5: يتم تهيئة العناصر الثلاثة للجدول names. كل عنصر "يشير" الآن إلى مصفوفة من العناصر من النوع string[]. names[i][j] هو الجدول من النوع string[] الذي يشير إليه names[i].
  • السطر 9: تهيئة العنصر names[i][j] داخل حلقة مزدوجة. هنا names[i] هو مصفوفة من i+1 عنصر. وبما أن names[i] هو مصفوفة، فإن names[i].Length هو عدد عناصرها.

فيما يلي مثال على الأنواع الثلاثة من الجداول التي عرضناها للتو:


using System;
 
namespace Chap1 {
     // tables
 
    using System;
 
     // test class
    public class P02 {
        public static void Main() {
             // an initialized 1-dimensional array
            int[] entiers = new int[] { 0, 10, 20, 30 };
            for (int i = 0; i < entiers.Length; i++) {
                Console.Out.WriteLine("entiers[{0}]={1}", i, entiers[i]);
            }//for
 
             // an initialized 2-dimensional array
            double[,] réels = new double[,] { { 0.5, 1.7 }, { 8.4, -6 } };
            for (int i = 0; i < réels.GetLength(0); i++) {
                for (int j = 0; j < réels.GetLength(1); j++) {
                    Console.Out.WriteLine("réels[{0},{1}]={2}", i, j, réels[i, j]);
                }//for j
            }//for i
 
             // a table of tables
            string[][] noms = new string[3][];
            for (int i = 0; i < noms.Length; i++) {
                noms[i] = new string[i + 1];
            }//for
             // initialization
            for (int i = 0; i < noms.Length; i++) {
                for (int j = 0; j < noms[i].Length; j++) {
                    noms[i][j] = "nom" + i + j;
                }//for j
            }//for i
             // display
            for (int i = 0; i < noms.Length; i++) {
                for (int j = 0; j < noms[i].Length; j++) {
                    Console.Out.WriteLine("noms[{0}][{1}]={2}", i, j, noms[i][j]);
                }//for j
            }//for i
         }//Main
     }//class
}//namespace

عند التنفيذ، نحصل على النتائج التالية:

entiers[0]=0
entiers[1]=10
entiers[2]=20
entiers[3]=30
réels[0,0]=0,5
réels[0,1]=1,7
réels[1,0]=8,4
réels[1,1]=-6
noms[0][0]=nom00
noms[1][0]=nom10
noms[1][1]=nom11
noms[2][0]=nom20
noms[2][1]=nom21
noms[2][2]=nom22

3.3. تعليمات C# الأساسية

يتم التمييز بين

1 الأوامر الأولية التي ينفذها الكمبيوتر.

2 تعليمات للتحكم في تسلسل البرنامج.

تتضح التعليمات الأولية عند النظر إلى بنية الحاسوب الصغير وأجهزته الطرفية.

  1. قراءة المعلومات من لوحة المفاتيح

  2. معالجة المعلومات

  3. كتابة المعلومات على الشاشة

3.3.1. الكتابة على الشاشة

هناك تعليمات مختلفة للكتابة على الشاشة:

Console.Out.WriteLine(expression)
Console.WriteLine(expression)
Console.Error.WriteLine (expression)

حيث التعبير هو أي نوع من البيانات يمكن تحويله إلى سلسلة لعرضه على الشاشة. تحتوي جميع كائنات C# أو .NET على دالة ToString() تُستخدم لإجراء هذا التحويل.

تتيح فئة System.Console الوصول إلى عمليات الكتابة على الشاشة (Write، WriteLine). تحتوي فئة Console على خاصيتين هما Out و Error اللتين تمثلان تدفق الكتابة من نوع TextWriter :

  • Console.WriteLine() تعادل Console.Out.WriteLine() وتكتب على Out المرتبط عادةً بالشاشة.
  • تكتب Console.Error.WriteLine() على دفق Error، الذي يرتبط عادةً بالشاشة.

يمكن إعادة توجيه تدفقات Out و Error إلى ملفات نصية أثناء تشغيل البرنامج، كما سنرى بعد قليل.

3.3.2. قراءة البيانات المكتوبة

يتم تعيين دفق البيانات من لوحة المفاتيح بواسطة الكائن Console.In من النوع TextReader. يمكن استخدام هذا النوع من الكائنات لقراءة سطر من النص باستخدام ReadLine :

    string ligne=Console.In.ReadLine();

توفر فئة Console طريقة ReadLine مرتبطة افتراضيًا بـ In. وبالتالي يمكننا كتابة:

    string ligne=Console.ReadLine();

يتم تخزين السطر الذي تمت كتابته على لوحة المفاتيح في المتغير ligne ويمكن بعد ذلك استخدامه بواسطة البرنامج. يمكن إعادة توجيه الدفق In إلى ملف، مثل Out و Error.

3.3.3. مثال على الإدخال والإخراج

فيما يلي برنامج قصير لتوضيح عمليات الإدخال/الإخراج للوحة المفاتيح/الشاشة:


using System;
 
namespace Chap1 {
     // test class
    public class P03 {
        public static void Main() {
 
             // write to Out feed
            object obj = new object();
            Console.Out.WriteLine(obj);
 
             // write to the Error stream
            int i = 10;
            Console.Error.WriteLine("i=" + i);
 
             // reading a line entered on the keyboard
            Console.Write("Tapez une ligne : ");
            string ligne = Console.ReadLine();
            Console.WriteLine("ligne={0}", ligne);
         }//fine hand
     }//end of class
}
  • السطر 9: obj هو مرجع كائن
  • السطر 10: يتم كتابة obj على الشاشة. بشكل افتراضي، يتم استدعاء obj.ToString().
  • السطر 14: يمكنك أيضًا كتابة:

            Console.Error.WriteLine("i={0}",i);

المعلمة الأولى "i={0}" هي تنسيق العرض، والمعلمات الأخرى هي التعبيرات المراد عرضها. عناصر {n} هي معلمات "موضعية". في وقت التشغيل، يتم استبدال المعلمة {n} بقيمة التعبير رقم n.

والنتيجة هي كما يلي:

1
2
3
4
System.Object
i=10
Tapez une ligne : je suis là
ligne=je suis là
  • السطر 1: العرض الناتج عن السطر 10 من الكود. عرضت وظيفة obj.ToString() اسم نوع المتغير obj: System.Object. نوع object هو اسم مستعار في C# لنوع .NET System.Object.

3.3.4. إعادة توجيه الإدخال/الإخراج

في نظامي DOS و UNIX، توجد ثلاثة أجهزة قياسية تسمى:

  1. جهاز الإدخال القياسي - يكون افتراضيًا لوحة المفاتيح ويحمل الرقم 0
  2. جهاز الإخراج القياسي - يكون افتراضيًا هو الشاشة ويحمل الرقم 1
  3. جهاز الخطأ القياسي - يكون افتراضيًا هو الشاشة ويحمل الرقم 2

في C#، يكتب Console.Out إلى الجهاز 1، ويتم كتابة دفق الكتابة Console.Error إلى الجهاز 2، ويقرأ دفق القراءة Console.In البيانات من الجهاز 0.

عند تشغيل برنامج في نظام Dos أو Unix، يمكنك تحديد الأجهزة 0 و 1 و 2 التي سيتم استخدامها للبرنامج الذي يتم تنفيذه. انظر إلى سطر الأوامر التالي:

pg arg1 arg2 .. argn

ووفقًا لبرنامج argi pg، يمكن إعادة توجيه أجهزة الإدخال/الإخراج القياسية إلى ملفات:

0<in.txt
يتم إعادة توجيه دفق الإدخال القياسي رقم 0 إلى in.txt. في البرنامج، سيأخذ Console.In بياناته من in.txt.
1>out.txt
يعيد توجيه الإخراج رقم 1 إلى الملف out.txt. وهذا يعني أن Console.Out في البرنامج سيكتب بياناته إلى الملف out.txt
1>>out.txt
كما سبق، ولكن البيانات المكتوبة تضاف إلى محتويات الملف الحالي out.txt.
2>error.txt
يعيد توجيه المخرج رقم 2 إلى الملف error.txt. وهذا يعني أن Console.Error في البرنامج ستكتب بياناتها إلى الملف error.txt
2>>error.txt
كما سبق، ولكن البيانات المكتوبة تضاف إلى محتويات الملف الحالي error.txt.
1>out.txt 2>error.txt
يتم إعادة توجيه الجهازين 1 و 2 إلى

لاحظ أنه لإعادة توجيه تدفقات الإدخال/الإخراج من pg إلى الملفات، لا يلزم تعديل pg. يحدد نظام التشغيل طبيعة الأجهزة الطرفية 0 و 1 و 2. انظر البرنامج التالي:


using System;
 
namespace Chap1 {
 
     // redirections
    public class P04 {
        public static void Main(string[] args) {
            // lecture flux In
            string data = Console.In.ReadLine();
             // write Out feed
            Console.Out.WriteLine("écriture dans flux Out : " + data);
            // écriture flux Error
            Console.Error.WriteLine("écriture dans flux Error : " + data);
         }//Main
     }//class
}

لنقم بإنشاء الملف القابل للتنفيذ لهذا الكود المصدري:

  • في [1]: يتم إنشاء الملف القابل للتنفيذ بالنقر بزر الماوس الأيمن على المشروع / Build
  • في [2]: في نافذة DOS، تم إنشاء الملف القابل للتنفيذ 04.exe في مجلد bin/Release الخاص بالمشروع.

دعونا نصدر الأوامر التالية في نافذة DOS [2]:

1
2
3
4
5
6
7
8
...\04\bin\Release>echo test >in.txt
...\04\bin\Release>more in.txt
test
...\04\bin\Release>04 0<in.txt 1>out.txt 2>err.txt
...\04\bin\Release>more out.txt
écriture dans flux Out : test
...\04\bin\Release>more err.txt
écriture dans flux Error : test
  • السطر 1: السلسلة test في ملف in.txt
  • السطران 2-3: عرض محتويات ملف in.txt للتحقق
  • السطر 4: تنفيذ البرنامج 04.exe. يتم إعادة توجيه التدفق In إلى in.txt، والتدفق Out إلى out.txt، والتدفق Error إلى err.txt. لا يؤدي التنفيذ إلى أي عرض.
  • السطران 5-6: محتويات ملف out.txt. يوضح لنا هذا المحتوى ما يلي:
  • تمت قراءة الملف in.txt
  • تمت إعادة توجيه عرض الشاشة إلى out.txt
  • السطران 7-8: فحص مماثل لملف err.txt

يمكننا أن نرى بوضوح أن التدفقين Out و In لا يكتبان إلى نفس الأجهزة، حيث تمت إعادة توجيههما بشكل منفصل.

3.3.5. تعيين قيمة تعبير إلى متغير

نحن مهتمون هنا بالعملية variable=expression;

يمكن أن يكون التعبير من النوع: حسابي، علائقي، منطقي، أحرف

3.3.5.1. تفسير عملية التعيين

العملية variable=expression;

هي في حد ذاتها تعبير يتم تقييمه على النحو التالي:

  • يتم تقييم الطرف الأيمن من عملية التعيين: والنتيجة هي قيمة V.
  • يتم تعيين القيمة V للمتغير
  • قيمة V هي أيضًا قيمة التعيين، والتي يُنظر إليها هذه المرة على أنها تعبير.

هذه هي الطريقة التي

    V1=V2=expression

صحيحًا. وبسبب الأسبقية، سيتم تقييم عامل = الموجود في أقصى اليمين. وبالتالي نحصل على

    V1=(V2=expression)

يتم تقييم التعبير V2=expression وتعيين القيمة V له. وقد أدى تقييم هذا التعبير إلى تعيين V إلى V2. ثم يتم تقييم عامل = التالي على النحو التالي:

    V1=V

لا تزال قيمة هذا التعبير هي V. ويؤدي حسابه إلى تعيين V إلى V1.

لذا فإن التعبير V1=V2=

هو تعبير يؤدي تقييمه إلى

  • يؤدي إلى قيمة التعبير للمتغيرين V1 و V2
  • تؤدي إلى قيمة التعبير.

ويمكن تعميم ذلك على تعبير مثل:

V1=V2=....=Vn=expression

3.3.5.2. التعبير الحسابي

المعاملات المستخدمة في التعبيرات الحسابية هي كما يلي:

  • الجمع

  • الطرح

* الضرب

/ القسمة: تكون النتيجة هي الناتج الدقيق إذا كان أحد المعاملين على الأقل عددًا حقيقيًا. إذا كان كلا المعاملين عددين صحيحين، تكون النتيجة هي الناتج الكامل. وبالتالي 5/2 -> 2 و 5.0/2 ->2.5.

% القسمة: تكون النتيجة هي الباقي، مهما كانت طبيعة المعاملات، ويكون الناتج عددًا صحيحًا. وبالتالي فهي عملية القسمة على القاسم.

هناك العديد من الدوال الرياضية. فيما يلي بعض منها:

double Sqrt(double x)
الجذر التربيعي
double Cos(double x)
جيب التمام
double Sin(double x)
جيب
تانغنت (double x)
الظل
double Pow(double x,double y)
x أس y (x>0)
double Exp(double x)
أسي
double Log(double x)
اللوغاريتم النيبري
double Abs(double x)
القيمة المطلقة

إلخ...

جميع هذه الدوال محددة في فئة C# تسمى Math. عند استخدامها، يجب أن تسبقها اسم الفئة التي تم تعريفها فيها. على سبيل المثال، اكتب:

double x, y=4;
x=Math.Sqrt(y);

التعريف الكامل لـ Math هو كما يلي:

Image

Image

Image

Image

Image

3.3.5.3. أولويات تقييم التعبيرات الحسابية

أولوية العوامل عند تقييم تعبير حسابي هي كما يلي (من الأعلى إلى الأقل أولوية):

[fonctions], [ ( )],[ *, /, %], [+, -]

تتمتع العوامل الحسابية الموجودة في نفس كتلة [ ] بنفس الأولوية.

3.3.5.4. التعبيرات العلائقية

المعاملات هي كما يلي:

       <, <=, ==, !=, >, >=

أولويات العوامل

>, >=, <, <=
==, !=

نتيجة التعبير العلائقي هي القيمة المنطقية "خطأ" إذا كان التعبير خطأ، و"صحيح" أو غير ذلك.

      bool fin;
      int x=...;
      fin=x>4;

مقارنة بين خاصيتين

لننظر إلى خاصيتين C1 و C2. يمكن مقارنتهما باستخدام العوامل

    <, <=, ==, !=, >, >=

ثم تتم مقارنة رموز Unicode الخاصة بهما، وهي أرقام. في ترتيب Unicode، لدينا العلاقات التالية:

espace < .. < '0' < '1' < .. < '9' < .. < 'A' < 'B' < .. < 'Z' < .. < 'a' < 'b' < .. <'z'

مقارنة سلسلتين

يتم مقارنتهما حرفًا بحرف. أول عدم تساوي يتم العثور عليه بين حرفين يؤدي إلى عدم تساوي بنفس المعنى في السلسلتين.

أمثلة:

قارن بين السلسلتين "Cat" و "Dog"

هذه المعادلة الأخيرة تعني أن "Cat" < "Dog".

قارن بين سلسلتي "Cat" و"Kitten". سيستمر التعادل طوال الوقت حتى تنفد سلسلة "Cat". في هذه الحالة، تُعلن السلسلة المنفدة على أنها "الأصغر". وبالتالي، لدينا العلاقة

    "Chat" < "Chaton".

وظائف لمقارنة سلسلتين

يمكنك استخدام العوامل العلائقية == و != لاختبار تساوي سلسلتين، أو فئة Equals في System.String. بالنسبة للعلاقات < <= > >=، استخدم فئة CompareTo في System.String:


using System;
 
namespace Chap1 {
    class P05 {
        static void Main(string[] args) {
            string chaine1="chat", chaine2="chien";
            int n = chaine1.CompareTo(chaine2);
            bool egal = chaine1.Equals(chaine2);
            Console.WriteLine("i={0}, egal={1}", n, egal);
            Console.WriteLine("chien==chaine1:{0},chien!=chaine2:{1}", "chien"==chaine1,"chien" != chaine2);
        }
    }
}

السطر 7، سيكون للمتغير i القيمة:

    0    إذا كانت السلسلتان متساويتين

    1    إذا كانت القناة 1 &gt; القناة 2

    -1    إذا كانت السلسلة رقم 1 &lt; السلسلة رقم 2

السطر 8، ستكون قيمة المتغير egal هي true إذا كانت السلسلتان متساويتين، وfalse في حالة عدم التساوي. يستخدم السطر 10 علامتي == و != للتحقق مما إذا كانت السلسلتان متساويتين أم لا.

نتائج التنفيذ:

i=-1, egal=False
chien==chaine1:False,chien!=chaine2:False

3.3.5.5. التعبيرات المنطقية

المعاملات التي يمكن استخدامها هي AND (&&) OR(||) NOT (!). نتيجة التعبير المنطقي هي قيمة منطقية.

أولويات العوامل :

  1. !
  2. &&
  3. ||

            double x = 3.5;
            bool valide = x > 2 && x < 4;

تحتوي العوامل العلائقية على عوامل الأولوية && و ||.

3.3.5.6. معالجة البتات

المشغلات

لنفترض أن i و j عددان صحيحان.

i<<n
يحول i n بتات إلى اليسار. البتات الواردة هي أصفار.
i>>n
يحول i n بتات إلى اليمين. إذا كان i عددًا صحيحًا موقّعًا (signed char، int، long)، يتم الحفاظ على بت الإشارة.
i & j
تقوم بإجراء عملية ET المنطقية لـ i و j بتًا بتًا.
i | j
تقوم بإجراء عملية OU المنطقية لـ i و j بتًا بتًا.
~i
يعكس i إلى 1
i^j
يقوم بعملية OU EXCLUSIF لـ i و j

أو الكود التالي:


short i = 100, j = -13;
ushort k = 0xF123;
Console.WriteLine("i=0x{0:x4}, j=0x{1:x4}, k=0x{2:x4}", i,j,k);
Console.WriteLine("i<<4=0x{0:x4}, i>>4=0x{1:x4},k>>4=0x{2:x4},i&j=0x{3:x4},i|j=0x{4:x4},~i=0x{5:x4},j<<2=0x{6:x4},j>>2=0x{7:x4}", i << 4, i >> 4, k >> 4, (short)(i & j), (short)(i | j), (short)(~i), (short)(j << 2), (short)(j >> 2));
  • يعرض التنسيق {0:x4} المعلمة رقم 0 بتنسيق سداسي عشري (x) مع 4 أحرف (4).

والنتائج هي كما يلي:

i=0x0064, j=0xfff3, k=0xf123
i<<4=0x0640, i>>4=0x0006,k>>4=0x0f12,i&j=0x0060,i|j=0xfff7,~i=0xff9b,j<<2=0xffcc,j>>2=0xfffc

3.3.5.7. تركيبة العوامل

a=a+b يمكن كتابتها على النحو التالي a+=b

يمكن كتابة a=a-b على النحو التالي: a-=b

وينطبق الأمر نفسه على العوامل /، %، *، <<، >>، &، |، ^. لذا يمكن كتابة a=a/2؛ على النحو التالي: a/=2؛

3.3.5.8. عمليات الزيادة والنقصان

يعني التعبير variable++ أن variable=variable+1 أو حتى variable+=1

تقييم المتغير -- يعني المتغير = المتغير - 1 أو حتى المتغير - = 1.

3.3.5.9. المشغل الثلاثي؟

التعبير

    expr_cond ? expr1:expr2

يتم تقييمه على النحو التالي:

1 يتم تقييم التعبير expr_cond. وهو تعبير شرطي يحتوي على

2 إذا كان صحيحًا، فإن قيمة التعبير هي قيمة expr1 ولا يتم تقييم expr2.

3 إذا كان كاذبًا، يحدث العكس: تكون قيمة التعبير هي قيمة expr2 ولا يتم تقييم expr1.

سيتم تعيين العملية i=(j>4 ? j+1:j-1) للمتغير i: j+1 إذا كان j>4، وj-1 في الحالات الأخرى. وهذا يماثل كتابة if(j>4) i=j+1; else i=j-1؛ ولكنه أكثر إيجازًا.

3.3.5.10. الأولوية العامة للمشغلات

() []  fonction
gd
! ~ ++ --
dg
جديد (نوع) عوامل التحويل
dg
*  /  %
gd
+  -
gd
<<  >>
gd
< <=  > >= instanceof
gd
==    !=
gd
&
gd
^
gd
|
gd
&&
gd
||
gd
?   :
dg
= += -= إلخ. .
dg

gd تشير إلى أنه في حالة تساوي الأولوية، يتم اتباع قاعدة الأولوية من اليسار إلى اليمين. وهذا يعني أنه عند وجود عوامل ذات أولوية متساوية في تعبير ما، يتم تقييم العامل الموجود في أقصى اليسار في التعبير أولاً. dg تشير إلى قاعدة الأولوية من اليمين إلى اليسار.

3.3.5.11. تغييرات النوع

في التعبير، يمكنك تغيير ترميز القيمة مؤقتًا. يُعرف هذا بتغيير نوع البيانات أو تحويل النوع. صيغة تغيير نوع القيمة في التعبير هي التالي:

    (type) valeur

ثم تتخذ القيمة النوع المشار إليه. ويؤدي ذلك إلى تغيير ترميز القيمة.


using System;
 
namespace Chap1 {
    class P06 {
        static void Main(string[] args) {
            int i = 3, j = 4;
            float f1=i/j;
            float f2=(float)i/j;
            Console.WriteLine("f1={0}, f2={1}",f1,f2);
        }
    }
}
  • السطر 7، ستكون قيمة f1 هي 0.0. القسمة 3/4 هي قسمة عددية لأن كلا المعاملين من النوع int.
  • السطر 8، (float)i هي قيمة i المحولة إلى float. الآن لدينا قسمة بين عدد حقيقي من النوع float وعدد صحيح من النوع int. هذه هي القسمة بين الأعداد الحقيقية. سيتم أيضًا تحويل قيمة j إلى float، ثم قسمة العددين الحقيقيين. ستكون قيمة f2 عندئذٍ 0.75.

فيما يلي النتائج:

f1=0, f2=0,75

في (float)i :

  • i هي قيمة مرمزة بدقة من 2 بايت
  • (float) i هي نفس القيمة المشفرة كتقريب حقيقي مكون من 4 بايت

قيمة i. لا يحدث هذا التحويل إلا خلال فترة الحساب، ويحتفظ المتغير i دائمًا بنوعه int.

3.4. تعليمات التحكم في تدفق البرنامج

3.4.1. توقف

يؤدي الأمر Exit المحدد في البيئة إلى إيقاف تنفيذ البرنامج.

syntaxe        void Exit(int status)
action        arrête le processus en cours et rend la valeur status au processus père

تقوم Exit بإنهاء العملية الحالية وإعادة التحكم إلى العملية المستدعية. ويمكن لهذه الأخيرة استخدام قيمة status. في نظام DOS، يتم عرض متغير status هذا في متغير النظام ERRORLEVEL الذي يمكن اختبار قيمته في ملف دفعي. وفي نظام Unix، مع مترجم الأوامر Shell Bourne، يتم عرض متغير $? الذي يسترد قيمة status.

    Environment.Exit(0);

سيوقف تنفيذ البرنامج بقيمة حالة تساوي 0.

3.4.2. بنية الاختيار البسيطة

 syntaxe :  if (condition) {actions_condition_vraie;} else {actions_condition_fausse;}

ملاحظات:

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

المكافئ الخوارزمي لهذه البنية هو if .. then ... otherwise :

مثال


    if (x>0)  { nx=nx+1;sx=sx+x;} else dx=dx-x;

يمكن تداخل هياكل الاختيار:

if(condition1)
if (condition2)
        {......}
      else         //condition2
         {......}
else         //condition1
     {.......}

تظهر المشكلة التالية أحيانًا:


using System;
 
namespace Chap1 {
    class P07 {
        static void Main(string[] args) {
            int n = 5;
            if (n > 1)
                if (n > 6)
                    Console.Out.WriteLine(">6");
                else Console.Out.WriteLine("<=6");
        }
    }
}

في المثال السابق، إلى أي جملة if تشير عبارة else في السطر 10؟ القاعدة هي أن عبارة else تشير دائمًا إلى أقرب جملة if: if(n>6)، السطر 8، في المثال. لنأخذ مثالًا آخر:


            if (n2 > 1) {
                if (n2 > 6) Console.Out.WriteLine(">6");
       } else Console.Out.WriteLine("<=1");    

هنا أردنا وضع جملة "else" في حالة if(n2>1) وعدم وضع "else" في حالة if(n2>6). وبسبب الملاحظة السابقة، فإننا مضطرون إلى وضع أقواس في if(n2>1) {...} else ...

3.4.3. بنية الحالة

الصيغة هي كما يلي:

switch(expression) {
    case v1:     
            actions1;
            break;
    case v2:     
            actions2;
            break;
         . .. .. .. .. ..
    default:     
            actions_sinon;
            break;
}

ملاحظات

  • يمكن أن تكون قيمة المفتاح عددًا صحيحًا أو حرفًا أو سلسلة
  • يُحاط التعبير بأقواس.
  • قد لا توجد الجملة default.
  • القيم vi هي القيم المحتملة للتعبير. إذا كانت قيمة التعبير هي vi ، يتم تنفيذ الإجراءات الموجودة خلف الجملة case vi.
  • تنقلنا التعليمات break خارج بنية case.
  • يجب أن تنتهي كل كتلة تعليمات مرتبطة بقيمة vi بتعليمات فرع (break، goto، return، ...) وإلا فإن المُترجم يُبلغ عن خطأ.

مثال

زيارة الخوارزميات

                selon la valeur de choix
                    cas 0
                         fin du module
                    cas 1
                         exécuter module M1
                    cas 2
                        exécuter module M2
                    sinon
                         erreur<--vrai
                findescas

في C#

int choix = 2; 
bool erreur = false;
switch (choix) {
   case 0: return;
   case 1: M1(); break;
   case 2: M2(); break;
   default: erreur = true; break;
}
}// end Main

static void M1() {
    Console.WriteLine("M1");
}

static void M2() {
    Console.WriteLine("M2");
}
}

3.4.4. تكرار الهياكل

3.4.4.1. عدد التكرارات المعروف

هيكل لـ

الصيغة هي كما يلي:


    for (i=id;i<=if;i=i+ip){ 
       actions; 
      } 

ملاحظات

  • يتم وضع الحجج الثلاثة لـ for بين قوسين وتفصل بينها فواصل منقوطة.
  • تنتهي كل عبارة for بفاصلة منقوطة.
  • لا تكون الأقواس الضيقة ضرورية إلا إذا كان هناك أكثر من إجراء واحد.
  • لا يتبع القوس المزدوج فاصلة منقوطة.

المكافئ الخوارزمي هو for :

pour i variant de id à if avec un pas de ip
    actions
finpour

والتي يمكن ترجمتها على النحو التالي:

    i  id
    tantque i<=if
        actions
        i i+ip
    fintantque

بنية foreach

الصيغة هي كما يلي:


foreach (Type variable in collection)
    instructions; 
}

ملاحظات

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

وبالتالي، فإن الكود التالي:


            string[] amis = { "paul", "hélène", "jacques", "sylvie" };
            foreach (string nom in amis) {
                Console.WriteLine(nom);
         }

سيعرض:

paul
hélène
jacques
sylvie

3.4.4.2. عدد التكرارات غير معروف

هناك العديد من البنيات في C# لهذه الحالة.

بنية tantque (while)


    while(condition){
          actions;
        } 

نقوم بالتكرار طالما تم التحقق من الشرط. قد لا يتم تنفيذ الحلقة أبدًا.

ملاحظات:

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

البنية الخوارزمية المقابلة هي tantque :

tantque condition
    actions
fintantque

تكرار البنية حتى (do while)

الصيغة هي كما يلي:


    do{
       instructions;
    }while(condition); 

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

ملاحظات

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

البنية الخوارزمية المقابلة هي repeat ... until :

répéter
    actions
jusqu'à condition

هيكل لـ general (for)

الصيغة هي كما يلي:


for(instructions_départ;condition;instructions_fin_boucle){
    instructions; 
}

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

ملاحظات

  • يتم فصل التعليمات المختلفة في instructions_depart و instructions_fin_boucle بفواصل.

والهيكل الخوارزمي المقابل هو كما يلي:

instructions_départ
tantque condition
    actions
    instructions_fin_boucle
fintantque

أمثلة

تحسب جميع أجزاء الكود التالية مجموع أول 10 أعداد صحيحة.

            int i, somme, n=10;
            for (i = 1, somme = 0; i <= n; i = i + 1)
                somme = somme + i;

            for (i = 1, somme = 0; i <= n; somme = somme + i, i = i + 1) ;

            i = 1; somme = 0;
            while (i <= n) { somme += i; i++; }

            i = 1; somme = 0;
            do somme += i++;
            while (i <= n);

3.4.4.3. تعليمات إدارة الحلقات

break
يخرج من الحلقات loop و while و do ... while.
continue
يواصل الحلقات for و while و do ... while إلى التكرار التالي. while

3.5. معالجة الاستثناءات

من المرجح أن تولد العديد من وظائف C# استثناءات، أي أخطاء. عندما يكون من المرجح أن تولد وظيفة ما استثناءً، يجب على المبرمج معالجته بهدف الحصول على برامج أكثر مقاومة للأخطاء: يجب عليك دائمًا تجنب "تعطل" التطبيق بشكل مفاجئ.

يتم التعامل مع الاستثناء على النحو التالي:

try{
    code susceptible de générer une exception
} catch (Exception e){
    traiter l'exception e
}
instruction suivante

إذا لم تولد الدالة استثناءً، ننتقل إلى التعليمات التالية، وإلا ننتقل إلى نص جملة catch ثم إلى التعليمات التالية. لاحظ النقاط التالية:

  • e هو كائن من نوع Exception أو مشتق منه. يمكنك أن تكون أكثر دقة باستخدام أنواع مثل IndexOutOfRangeException و FormatException و SystemException وغيرها: فهناك عدة أنواع من الاستثناءات. بكتابة catch (Exception e)، تشير إلى أنك تريد معالجة جميع أنواع الاستثناءات. إذا كان من المحتمل أن يولد الكود الموجود في try عدة أنواع من الاستثناءات، فقد ترغب في أن تكون أكثر دقة من خلال إدارة الاستثناء باستخدام عدة catch:
try{
    code susceptible de générer les exceptions
} catch ( IndexOutOfRangeException e1){
    traiter l'exception e1
}
} catch ( FormatException e2){
    traiter l'exception e2
}
instruction suivante
  • يمكننا إضافة جملة finally إلى جملتي try/catch:
try{
    code susceptible de générer une exception
} catch (Exception e){
    traiter l'exception e
}
finally{
    code exécuté après try ou catch
}
instruction suivante

سواء كان هناك استثناء أم لا، سيتم دائمًا تنفيذ جملة code finally.

  • في جملة catch، قد لا ترغب في استخدام الاستثناء المتاح. بدلاً من كتابة catch (Exception e){..}، نكتب catch(Exception){...} أو ببساطة catch {...}.
  • تحتوي فئة Exception على خاصية Message وهي رسالة توضح تفاصيل الخطأ الذي حدث. لذا، إذا أردنا عرض هذه الرسالة، فسنكتب:
catch (Exception ex){
    Console.WriteLine("L'erreur suivante s'est produite : {0}",ex.Message);
    ...
}//catch
  • تحتوي فئة Exception على طريقة ToString التي تُرجع سلسلة تشير إلى نوع الاستثناء وقيمة الرسالة. وبالتالي يمكننا كتابة:
catch (Exception ex){
    Console.WriteLine("L'erreur suivante s'est produite : {0}", ex.ToString());
    ...
}//catch

يمكننا أيضًا كتابة:

catch (Exception ex){
    Console.WriteLine("L'erreur suivante s'est produite : {0}",ex);
    ...
}//catch

سيقوم المُترجم بتعيين القيمة ex.ToString() للمعلمة {0}.

يوضح المثال التالي استثناءً ناتجًا عن استخدام عنصر مصفوفة غير موجود:


using System;
 
namespace Chap1 {
    class P08 {
        static void Main(string[] args) {
             // declaring & initializing an array
            int[] tab = { 0, 1, 2, 3 };
            int i;
             // table display with for
            for (i = 0; i < tab.Length; i++)
                Console.WriteLine("tab[{0}]={1}", i, tab[i]);
             // table display with a for each
            foreach (int élmt in tab) {
                Console.WriteLine(élmt);
            }
             // generating an exception
            try {
                tab[100] = 6;
            } catch (Exception e) {
                Console.Error.WriteLine("L'erreur suivante s'est produite : " + e);
                return;
             }//try-catch
            finally {
                Console.WriteLine("finally ...");
            }
        }
    }
}

في الأعلى، ستولد السطر 18 استثناءً لأن مصفوفة tab لا تحتوي على العنصر رقم 100. يؤدي تشغيل البرنامج إلى النتائج التالية:

tab[0]=0
tab[1]=1
tab[2]=2
tab[3]=3
0
1
2
3
L'erreur suivante s'est produite : System.IndexOutOfRangeException: L'index se trouve en dehors des limites du tableau.
   à Chap1.P08.Main(String[] args) dans C:\data\travail\2007-2008\c# 2008\poly\Chap1\08\Program.cs:ligne 7
finally ...
  • السطر 9: حدث استثناء [System.IndexOutOfRangeException]
  • السطر 11: تم تنفيذ جملة finally (الأسطر 23-25) من الكود، على الرغم من أن السطر 21 احتوى على تعليمات return للخروج من الأسلوب. لاحظ أن finally يتم تنفيذه دائمًا.

فيما يلي مثال آخر على كيفية معالجة الاستثناء الناتج عن تعيين سلسلة إلى متغير عدد صحيح عندما لا تمثل السلسلة عددًا صحيحًا:


using System;
 
namespace Chap1 {
    class P08 {
        static void Main(string[] args) {
 
             // example 2
             // We ask for the name
            Console.Write("Nom : ");
             // reading response
            string nom = Console.ReadLine();
             // age requested
            int age = 0;
            bool ageOK = false;
            while (!ageOK) {
                 // question
                Console.Write("âge : ");
                 // read-verify answer
                try {
                    age = int.Parse(Console.ReadLine());
                    ageOK = age>=1;
                } catch {
                 }//try-catch
                if (!ageOK) {
                    Console.WriteLine("Age incorrect, recommencez...");
                }
             }//while
             // final display
            Console.WriteLine("Vous vous appelez {0} et vous avez {1} an(s)",nom,age);
        }
    }
}
  • الأسطر 15-27: الحلقة الخاصة بإدخال عمر الشخص
  • السطر 20: يتم تحويل السطر المكتوب على لوحة المفاتيح إلى عدد صحيح باستخدام int.Parse. ترمي هذه الطريقة استثناءً إذا تعذر التحويل. ولهذا السبب تم وضع العملية في try / catch.
  • السطور 22-23: إذا تم إلقاء استثناء، ننتقل إلى catch حيث لا يتم فعل أي شيء. وبالتالي، فإن المتغير المنطقي ageOK الموجود في السطر 14، والذي تم تعيينه على false، سيبقى على false.
  • السطر 21: إذا وصلت إلى هذا السطر، فهذا يعني أن تحويل السلسلة إلى عدد صحيح (string -> int) قد نجح. ومع ذلك، تحقق من أن العدد الصحيح الذي تم الحصول عليه أكبر من أو يساوي 1.
  • الأسطر 24-26: يتم إصدار رسالة خطأ إذا كان العمر غير صحيح.

بعض نتائج الأداء:

1
2
3
Nom : dupont
âge : 23
Vous vous appelez dupont et vous avez 23 an(s)
1
2
3
4
5
6
7
Nom : durand
âge : x
Age incorrect, recommencez...
âge : -4
Age incorrect, recommencez...
âge : 12
Vous vous appelez durand et vous avez 12 an(s)

3.6. مثال على التطبيق - V1

نقترح كتابة برنامج لحساب ضريبة الدخل للمكلف. الحالة المبسطة هي حالة مكلف لا يعلن سوى عن راتبه (أرقام عام 2004 لدخل عام 2003):

  • يُحسب عدد حصص الموظف على النحو التالي: nbParts=nbEnfants/2 +1 إذا كان غير متزوج، و nbEnfants/2+2 إذا كان متزوجًا، حيث nbEnfants هو عدد أطفاله.
  • إذا كان لديه ثلاثة أطفال على الأقل، يحصل على نصف حصة إضافية
  • احسب دخلك الخاضع للضريبة R=0.72*S، حيث S هو راتبه السنوي
  • احسب معامل الأسرة QF=R/nbParts
  • احسب ضريبتك I. انظر الجدول التالي:
4262
0
0
8382
0.0683
291.09
14753
0.1914
1322.92
23888
0.2826
2668.39
38868
0.3738
4846.98
47932
0.4262
6883.66
0
0.4809
9505.54

يحتوي كل سطر على 3 حقول. لحساب الضريبة I، ابحث عن السطر الأول حيث QF<=champ1. على سبيل المثال، إذا كان QF=5000، نجد السطر

    8382        0.0683        291.09

الضريبة I تساوي إذن 0.0683*R - 291.09*nbParts. إذا كان QF بحيث لا يتم التحقق أبدًا من العلاقة QF<=champ1، يتم استخدام معاملات السطر الأخير. هنا:

0                0.4809    9505.54

مما يعطي الضريبة I=0.4809*R - 9505.54*nbParts.

البرنامج المقابل بلغة C# هو كما يلي:


using System;
 
namespace Chap1 {
    class Impots {
        static void Main(string[] args) {
             // data tables required for tax calculation
            decimal[] limites = { 4962M, 8382M, 14753M, 23888M, 38868M, 47932M, 0M };
            decimal[] coeffR = { 0M, 0.068M, 0.191M, 0.283M, 0.374M, 0.426M, 0.481M };
            decimal[] coeffN = { 0M, 291.09M, 1322.92M, 2668.39M, 4846.98M, 6883.66M, 9505.54M };
 
             // marital status is restored
            bool OK = false;
            string reponse = null;
            while (!OK) {
                Console.Write("Etes-vous marié(e) (O/N) ? ");
                reponse = Console.ReadLine().Trim().ToLower();
                if (reponse != "o" && reponse != "n")
                    Console.Error.WriteLine("Réponse incorrecte. Recommencez");
                else OK = true;
             }//while
            bool marie = reponse == "o";
 
             // number of children
            OK = false;
            int nbEnfants = 0;
            while (!OK) {
                Console.Write("Nombre d'enfants : ");
                try {
                    nbEnfants = int.Parse(Console.ReadLine());
                    OK = nbEnfants >= 0;
                } catch {
                 }// try
                if (!OK) {
                    Console.WriteLine("Réponse incorrecte. Recommencez");
                }
             }// while
 
             // salary
            OK = false;
            int salaire = 0;
            while (!OK) {
                Console.Write("Salaire annuel : ");
                try {
                    salaire = int.Parse(Console.ReadLine());
                    OK = salaire >= 0;
                } catch {
                 }// try
                if (!OK) {
                    Console.WriteLine("Réponse incorrecte. Recommencez");
                }
             }// while
 
             // calculating the number of shares
            decimal nbParts;
            if (marie) nbParts = (decimal)nbEnfants / 2 + 2;
            else nbParts = (decimal)nbEnfants / 2 + 1;
            if (nbEnfants >= 3) nbParts += 0.5M;
 
             // taxable income
            decimal revenu = 0.72M * salaire;
 
             // family quotient
            decimal QF = revenu / nbParts;
 
             // search for tax bracket corresponding to QF
            int i;
            int nbTranches = limites.Length;
            limites[nbTranches - 1] = QF;
            i = 0;
            while (QF > limites[i]) i++;
             // tax
            int impots = (int)(coeffR[i] * revenu - coeffN[i] * nbParts);
 
             // the result is displayed
            Console.WriteLine("Impôt à payer : {0} euros", impots);
        }
    }
}
  • الأسطر 7-9: تُلحق القيم العددية بحرف M (Money) لتكون من النوع العشري.
  • السطر 16:
    • تجعل Console.ReadLine() السلسلة C1 مكتوبة
    • C1.Trim() تزيل المسافات في بداية ونهاية السلسلة C1 - تعرض سلسلة C2
    • C2.ToLower() تجعل السلسلة C3 وهي السلسلة C2 محولة إلى أحرف صغيرة.
  • السطر 21: تتلقى المتغير المنطقي marie القيمة true أو false بناءً على العلاقة answer=="o"
  • السطر 29: يتم تحويل السلسلة المكتوبة على لوحة المفاتيح إلى عدد صحيح. إذا فشل التحويل، يتم إصدار استثناء.
  • السطر 30: المتغير المنطقي OK يتلقى القيمة true أو false بناءً على العلاقة nbEnfants>=0
  • السطران 55-56: لا يمكنك ببساطة كتابة nbEnfants/2. إذا كان nbEnfants يساوي 3، فسيكون لدينا 3/2، وهو قسمة عددية ستعطي 1 وليس 1.5. لذلك نكتب (decimal)nbEnfants لجعل أحد معاملات القسمة عددًا حقيقيًا وبالتالي نحصل على قسمة بين الأعداد الحقيقية.

فيما يلي بعض الأمثلة:

Etes-vous marié(e) (O/N) ? o
Nombre d'enfants : 2
Salaire annuel : 60000
Impôt à payer : 4282 euros
Etes-vous marié(e) (O/N) ? oui
Réponse incorrecte. Recommencez
Etes-vous marié(e) (O/N) ? o
Nombre d'enfants : trois
Réponse incorrecte. Recommencez
Nombre d'enfants : 3
Salaire annuel : 60000 euros
Réponse incorrecte. Recommencez
Salaire annuel : 60000
Impôt à payer : 2959 euros

3.7. أهم حجج البرنامج

يمكن للوظيفة الرئيسية Main قبول مصفوفة من السلاسل كمعلمة: String[] (أو string[]). يحتوي هذا الجدول على حجج سطر الأوامر المستخدمة لتشغيل التطبيق. على سبيل المثال، إذا قمنا بتشغيل البرنامج P باستخدام الأمر التالي (Dos):


        P arg0 arg1 … argn

وإذا تم تعريف Main على النحو التالي:

public static void Main(string[] args)

args[0]="arg0", args[1]="arg1" ... إليك مثال:


using System;
 
namespace Chap1 {
    class P10 {
        static void Main(string[] args) {
             // list parameters received
            Console.WriteLine("Il y a  " + args.Length + " arguments");
            for (int i = 0; i < args.Length; i++) {
                Console.Out.WriteLine("arguments[" + i + "]=" + args[i]);
            }
        }
    }
}

لتمرير الحجج إلى الكود المنفذ، اتبع الخطوات التالية:

  • في [1]: انقر بزر الماوس الأيمن على المشروع / خصائص
  • في [2]: علامة التبويب [Debug]
  • في [3]: أدخل الحجج

يؤدي التنفيذ إلى النتائج التالية:

1
2
3
4
5
Il y a  4 arguments
arguments[0]=a0
arguments[1]=a1
arguments[2]=a2
arguments[3]=a3

لاحظ أن

public static void Main()

صالحًا إذا كانت Main لا تتوقع أي معلمات.

3.8. التعدادات

التعداد هو نوع بيانات يكون مجال قيمه مجموعة من الثوابت الصحيحة. لنفكر في برنامج عليه إدارة درجات امتحان. ستكون هناك خمس درجات: Fair، AssezBien، Fine، TrèsBien، Excellent.

يمكننا إذن تعريف تعداد لهذه الثوابت الخمس:


        enum Mentions { Passable, AssezBien, Bien, TrèsBien, Excellent };

داخليًا، يتم ترميز هذه الثوابت الخمسة بأعداد صحيحة متتالية تبدأ بـ 0 للثابت الأول، و 1 للثابت التالي، وهكذا... يمكن تعريف متغير بحيث يأخذ هذه القيم في التعداد:


            // une variable qui prend ses valeurs dans l'énumération Mentions
Mentions maMention = Mentions.Passable;

يمكن مقارنة متغير بالقيم المختلفة الممكنة في التعداد:


            if (maMention == Mentions.Passable) {
                Console.WriteLine("Peut mieux faire");
}

يمكنك الحصول على جميع قيم التعداد:


             // list of statements in string form
            foreach (Mentions m in Enum.GetValues(maMention.GetType())) {
                Console.WriteLine(m);
}

بالطريقة نفسها التي يكون فيها النوع البسيط int مكافئًا لـ System.Int32، يكون النوع البسيط enum مكافئًا لـ System.Enum. تحتوي هذه البنية على طريقة ثابتة GetValues تسمح لك بالحصول على جميع قيم نوع معدد يتم تمريره كمعلمة. يجب أن يكون هذا كائنًا من النوع Type الذي يمثل فئة من المعلومات حول نوع البيانات. يتم الحصول على نوع المتغير v بواسطة v.GetType(). يتم الحصول على نوع النوع T بواسطة typeof(T). لذا، هنا maMention.GetType() يعطي كائن Type التعداد Mentions و Enum.GetValues(maMention.GetType()) قائمة قيم التعداد Mentions.

إذا كتبنا الآن


             //list of mentions in integer form
            foreach (int m in Enum.GetValues(typeof(Mentions))) {
                Console.WriteLine(m);
}

السطر 2، متغير الحلقة من النوع الصحيح. ثم نحصل على قائمة قيم التعداد في شكل أعداد صحيحة. يتم الحصول على الكائن من النوع System.Type المطابق لنوع البيانات Mentions بواسطة typeof(Mentions). كان بإمكاننا كتابة maMention.GetType().

يبرز البرنامج التالي ما تمت كتابته للتو:


using System;
 
namespace Chap1 {
    class P11 {
        enum Mentions { Passable, AssezBien, Bien, TrèsBien, Excellent };
        static void Main(string[] args) {
             // a variable that takes its values from the Mentions enumeration
            Mentions maMention = Mentions.Passable;
             // variable value display
            Console.WriteLine("mention=" + maMention);
             // test with enumeration value
            if (maMention == Mentions.Passable) {
                Console.WriteLine("Peut mieux faire");
            }
             // list of statements in string form
            foreach (Mentions m in Enum.GetValues(maMention.GetType())) {
                Console.WriteLine(m);
            }
             //list of mentions in integer form
            foreach (int m in Enum.GetValues(typeof(Mentions))) {
                Console.WriteLine(m);
            }
        }
    }
}

النتائج هي كما يلي:

mention=Passable
Peut mieux faire
Passable
AssezBien
Bien
TrèsBien
Excellent
0
1
2
3
4

3.9. تمرير المعلمات إلى دالة

نحن مهتمون هنا بالطريقة التي يتم بها تمرير المعلمات عبر الدالة. لننظر إلى الدالة الثابتة التالية:


        private static void ChangeInt(int a) {
            a = 30;
            Console.WriteLine("Paramètre formel a=" + a);
}

في تعريف الدالة، السطر 1، يُسمى a بالمعلمة الشكلية. وهو موجود فقط لغرض تعريف الدالة changeInt. وكان من الممكن أن يُسمى b. لننظر الآن إلى أحد استخدامات هذه الدالة:


        public static void Main() {
            int age = 20;
            ChangeInt(age);
            Console.WriteLine("Paramètre effectif age=" + age);
}

هنا في التعليمات الموجودة في السطر 3، ChangeInt(age)، age هي المعلمة الفعلية التي ستنقل قيمتها إلى المعلمة الشكلية a. نحن مهتمون بكيفية استرجاع المعلمة الشكلية لقيمة المعلمة الفعلية.

3.9.1. التمرير بالقيمة

يوضح المثال التالي أنه، بشكل افتراضي، يتم تمرير معلمات الدالة بالقيمة، أي يتم نسخ قيمة المعلمة الفعلية إلى المعلمة الشكلية المقابلة. لدينا كيانان متميزان. إذا قامت الدالة بتعديل المعلمة الشكلية، تظل المعلمة الفعلية دون تغيير.


using System;
 
namespace Chap1 {
    class P12 {
        public static void Main() {
            int age = 20;
            ChangeInt(age);
            Console.WriteLine("Paramètre effectif age=" + age);
        }
        private static void ChangeInt(int a) {
            a = 30;
            Console.WriteLine("Paramètre formel a=" + a);
        }
    }
}

والنتائج هي كما يلي:

Paramètre formel a=30
Paramètre effectif age=20

تم نسخ القيمة 20 للمعلمة الفعلية age إلى المعلمة الشكلية a (السطر 10). ثم تم تعديلها (السطر 11). بقيت المعلمة الفعلية دون تغيير. هذا الوضع مناسب لمعلمات إدخال الدالة.

3.9.2. التمرير بالرجوع

في التشغيل بالمرجع، تكون المعلمة الفعالة والمعلمة الشكلية كيانًا واحدًا. إذا قامت الدالة بتعديل المعلمة الشكلية، يتم تعديل المعلمة الفعالة أيضًا. في C#، يجب أن يسبق كلاهما الكلمة الرئيسية ref :

إليك مثال على ذلك:


using System;
 
namespace Chap1 {
    class P12 {
        public static void Main() {
             // example 2
            int age2 = 20;
            ChangeInt2(ref age2);
            Console.WriteLine("Paramètre effectif age2=" + age2);
        }
        private static void ChangeInt2(ref int a2) {
            a2 = 30;
            Console.WriteLine("Paramètre formel a2=" + a2);
        }
    }
}

ونتيجة الأداء :

Paramètre formel a2=30
Paramètre effectif age2=30

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

3.9.3. التمرير بالرجوع باستخدام الكلمة الرئيسية out

لننظر إلى المثال السابق الذي لم يتم فيه تهيئة متغير age2 قبل استدعاء الدالة changeInt :


using System;
 
namespace Chap1 {
    class P12 {
        public static void Main() {
             // example 2
            int age2;
            ChangeInt2(ref age2);
            Console.WriteLine("Paramètre effectif age2=" + age2);
        }
        private static void ChangeInt2(ref int a2) {
            a2 = 30;
            Console.WriteLine("Paramètre formel a2=" + a2);
        }
    }
}

عندما نقوم بتجميع هذا البرنامج، نحصل على خطأ:

    Use of unassigned local variable 'age2'

يمكننا التغلب على هذه المشكلة عن طريق تعيين قيمة أولية لـ age2. يمكنك أيضًا استبدال الكلمة الرئيسية ref بالكلمة الرئيسية out. ثم نوضح أن المعلمة هي معلمة إخراج فقط وبالتالي لا تحتاج إلى قيمة أولية:


using System;
 
namespace Chap1 {
    class P12 {
        public static void Main() {
             // example 3
            int age3;
            ChangeInt3(out age3);
            Console.WriteLine("Paramètre effectif age3=" + age3);
        }
        private static void ChangeInt3(out int a3) {
            a3 = 30;
            Console.WriteLine("Paramètre formel a3=" + a3);
        }
    }
}

النتائج هي كما يلي:

Paramètre formel a3=30
Paramètre effectif age3=30