Skip to content

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

3.1. Introduction

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

  • البيانات
  • الأوامر التي تعالجها

نسعى عمومًا إلى فصل البيانات عن التعليمات:

3.2. بيانات C#

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

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

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

نوع C#
النوع .NET
البيانات الممثلة
اللاحقة
القيم الحرفية
الترميز
نطاق القيم
char
حرف (حروف)
حرف
 
2 بايت
حرف Unicode (UTF-16)
string
سلسلة (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]
byte
بايت (S)
..
 
1 بايت
[0 , 28 -1] [0,255]
short
Int16 (S)
..
 
2 بايت
[-215, 215-1] [-32768, 32767]
ushort
UInt16 (S)
..
 
2 بايت
[0, 216-1] [0,65535]
float
فردي (S)
عدد حقيقي
F
4 بايت
[1.5 10-45, 3.4 10+38] كقيمة مطلقة
double
مزدوج (S)
..
D
8 بايت
[-1.7 10+308, 1.7 10+308] كقيمة مطلقة
decimal
عشري (S)
رقم عشري
م
16 بايت
[1.0 10-28,7.9 10+28] كقيمة مطلقة مع 28 رقمًا معنويًا
bool
منطقية (S)
..
 
1 بايت
صحيح، خطأ
object
كائن (C)
مرجع الكائن
  
مرجع الكائن

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

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

تعد كل من structure و classe أنواعًا معقدة لها سمات وأساليب. وبالتالي، يمكننا كتابة:

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) {
            // مثال 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: إعلان عدد صحيح ent
  • السطر 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. ترميز البيانات الحرفية

entier int (32 bits)
145، -7، 0xFF (سداسي عشري)
entier long (64 bits) - suffixe L
100000L
réel double
134.789، -45E-18 (-45 × 10⁻¹⁸)
réel float (suffixe F)
134.789F، -45E-18F (-45 × 10⁻¹⁸)
réel decimal (suffixe M)
100000M
caractère char
'A'، 'b'
chaîne de caractères string
"اليوم" "c:\\chap1\\paragraph3" @"c:\chap1\paragraph3"
booléen bool
صحيح، خطأ
date
new DateTime(1954,10,13) (السنة، الشهر، اليوم) لـ 13/10/1954

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

3.2.3. إعلان البيانات

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

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

كما تسمح هذه الإعلانات للمترجم بالكشف عن أخطاء البرمجة. وهكذا فإن العملية

x=x*2;

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

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

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

    const type 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 و fin مختلفان.

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

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

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

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

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

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

فيما يلي مثال:


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: (بيانات).GetType().Name هو الاسم المختصر لـ (بيانات)، (بيانات).GetType().FullName هو الاسم الكامل لـ (بيانات)
  • السطر 8: بيانات مصنفة ضمناً. لأن 3 من النوع int، فإن j سيكون من النوع int.
  • السطر 10: بيانات مصنفة ضمناً. ولأن DateTime.Now من النوع DateTime، فإن aujourdhui سيكون من النوع 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 -> chaîne
nombre.ToString()
chaine -> int
int.Parse(سلسلة) أو System.Int32.Parse
chaîne -> long
long.Parse(سلسلة) أو System.Int64.Parse
chaîne -> double
double.Parse (سلسلة) أو System.Double.Parse (سلسلة)
chaîne -> float
float.Parse (سلسلة) أو System.Float.Parse (سلسلة)

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


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

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


using System;

namespace Chap1 {
    class P01 {
        static void Main(string[] args) {

            // بيانات
            const int i = 10;
            const long l = 100000;
            const float f = 45.78F;
            double d = -14.98;

            // رقم --> سلسلة
            affiche(i.ToString());
            affiche(l.ToString());
            affiche(f.ToString());
            affiche(d.ToString());

            //منطقية --> سلسلة
            const bool b = false;
            affiche(b.ToString());

            // سلسلة --> عدد صحيح
            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);
            }

            // سلسلة --> طويل
            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);
            }

            // سلسلة --> دبل
            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);
            }

            // سلسلة --> عدد عائم
            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);
            }

        }// نهاية main

        public static void affiche(string S) {
            Console.Out.WriteLine("S={0}",S);
        }
    }// نهاية الفئة
}

الأسطر 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 هو عدد البيانات التي يمكن أن يحتوي عليها المصفوف. تشير صيغة Tableau[i] إلى البيانات رقم i حيث i ينتمي إلى النطاق [0,n-1]. أي إشارة إلى البيانات Tableau[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 هو عدد الأعمدة. تشير صيغة Tableau[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 صفوف. كل عنصر tableau[i] هو مرجع مصفوفة أحادي البعد. لا يتم تهيئة هذه المراجع tableau[i] عند الإعلان أعلاه. قيمتها هي المرجع null.

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


            // مصفوفة من المصفوفات
            string[][] noms = new string[3][];
            for (int i = 0; i < noms.Length; i++) {
                noms[i] = new string[i + 1];
            }//for
            // التهيئة
            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: مصفوفة noms مكونة من 3 عناصر من النوع string[][]. كل عنصر هو مؤشر مصفوفة (مرجع كائن) عناصره من النوع string[].
  • الأسطر 3-5: يتم تهيئة العناصر الثلاثة في المصفوفة noms. ويشير كل منها الآن إلى مصفوفة من العناصر من النوع string[]. noms[i][j] هو العنصر j من المصفوفة من النوع string [] المشار إليها بواسطة noms[i].
  • السطر 9: تهيئة العنصر noms[i][j] داخل حلقة مزدوجة. هنا، noms[i] هو مصفوفة من i+1 عنصر. وبما أن noms[i] هو مصفوفة، فإن noms[i].Length هو عدد عناصرها.

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


using System;

namespace Chap1 {
    // مصفوفات

    using System;

    // فئة الاختبار
    public class P02 {
        public static void Main() {
            // مصفوفة أحادية البعد تم تهيئتها
            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]);
            }//لـ

            // مصفوفة ثنائية الأبعاد تم تهيئتها
            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

            // مصفوفة من المصفوفات
            string[][] noms = new string[3][];
            for (int i = 0; i < noms.Length; i++) {
                noms[i] = new string[i + 1];
            }//لـ
            // التهيئة
            for (int i = 0; i < noms.Length; i++) {
                for (int j = 0; j < noms[i].Length; j++) {
                    noms[i][j] = "nom" + i + j;
                }//لـ j
            }//لـ i
            // عرض
            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]);
                }//لـ j
            }//لـ i
        }//الرئيسي
    }//فئة
}//مساحة الاسم

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

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)

حيث 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 {
    // فئة الاختبار
    public class P03 {
        public static void Main() {

            // الكتابة على دفق Out
            object obj = new object();
            Console.Out.WriteLine(obj);

            // الكتابة على دفق Error
            int i = 10;
            Console.Error.WriteLine("i=" + i);

            // قراءة سطر تم إدخاله من لوحة المفاتيح
            Console.Write("Tapez une ligne : ");
            string ligne = Console.ReadLine();
            Console.WriteLine("ligne={0}", ligne);
        }//نهاية main
    }//نهاية الفئة
}
  • السطر 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 {

    // عمليات إعادة التوجيه
    public class P04 {
        public static void Main(string[] args) {
            // قراءة تدفق In
            string data = Console.In.ReadLine();
            // كتابة تدفق Out
            Console.Out.WriteLine("écriture dans flux Out : " + data);
            // كتابة تدفق Error
            Console.Error.WriteLine("écriture dans flux Error : " + data);
        }//الرئيسي
    }//فئة
}

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

  • في [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=expression

هي تعبير يؤدي تقييمه

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

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

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 Tan(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. التعبيرات العلائقية

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

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

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

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

نتيجة التعبير العلائقي هي القيمة المنطقية false إذا كان التعبير خاطئًا، و true خلاف ذلك.

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

مقارنة حرفين

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

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

يتم عندئذٍ مقارنة رموز Unicode الخاصة بهما، وهي أرقام. وفقًا لترتيب Unicode، نحصل على العلاقات التالية:

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

مقارنة سلسلتين من الأحرف

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

أمثلة:

لنفترض أننا نقارن السلسلتين "Chat" و "Chien"

هذا الاختلاف الأخير يسمح بالقول إن "Chat" < "Chien".

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

    "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 > السلسلة رقم 2

-1 إذا كانت السلسلة رقم 1 < السلسلة رقم 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
يقوم بإجراء عملية منطقية بين i و j بتًا بتًا.
i | j
يقوم بإجراء عملية منطقية بين 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

الترميز variable-- يعني variable=variable-1 أو variable-=1.

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

التعبير

    expr_cond ? expr1:expr2

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

1 يتم تقييم التعبير expr_cond. وهو تعبير شرطي بقيمة vrai أو faux

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
new (type) opérateurs cast       
dg
*  /  %                          
gd
+  -                             
gd
<<  >>                           
gd
< <=  > >= instanceof            
gd
==    !=                         
gd
&                                
gd
^                                
gd
|                                
gd
&&                               
gd
||                               
gd
?   :                            
dg
= += -= etc. .                   
dg

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

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

من الممكن، في تعبير ما، تغيير ترميز قيمة ما مؤقتًا. ويُطلق على ذلك تغيير نوع البيانات أو بالإنجليزية type casting. صيغة تغيير نوع قيمة ما في تعبير ما هي كما يلي:

    (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 المحددة في الفئة Environment بإيقاف تنفيذ البرنامج.

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

تؤدي Exit إلى إنهاء العملية الجارية وإعادة السيطرة إلى العملية المستدعية. يمكن استخدام قيمة status من قبل هذه العملية. في DOS، يتم إرجاع متغير الحالة هذا في متغير النظام 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 .. thenelse:

مثال


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

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

if(condition1)
if (condition2)
        {......}
      else         //الشرط 2
         {......}
else         //الشرط 1
     {.......}

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


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;
}

ملاحظات

  • يمكن أن تكون قيمة تعبير التحكم في switch عددًا صحيحًا أو حرفًا أو سلسلة أحرف
  • يُحاط تعبير التحكم بأقواس.
  • قد تكون العبارة default غائبة.
  • القيم vi هي قيم محتملة للتعبير. إذا كانت قيمة التعبير هي vi ، يتم تنفيذ الإجراءات الموجودة خلف جملة case vi.
  • تؤدي التعليمات break إلى الخروج من بنية الحالة.
  • يجب أن تنتهي كل مجموعة تعليمات مرتبطة بقيمة 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;
}
}// نهاية Main

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

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

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

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

هيكل for

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


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

ملاحظات

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

المكافئ الخوارزمي هو البنية pour:

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

والتي يمكن ترجمتها إلى بنية tantque:

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

بنية foreach

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


foreach (Type variable in collection)
    instructions; 
}

ملاحظات

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

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


            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# لهذه الحالة.

بنية while


    while(condition){
          actions;
        } 

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

ملاحظات:

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

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

tantque condition
    actions
fintantque

هيكل التكرار حتى (do while)

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


    do{
       instructions;
    }while(condition); 

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

ملاحظات

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

البنية الخوارزمية المقابلة هي بنية تكرار ... حتى:

répéter
    actions
jusqu'à condition

بنية for (for)

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


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

يتم تنفيذ

يتم تكرار الحلقة طالما أن الشرط صحيح (يتم تقييمه قبل كل دورة من دورات الحلقة). يتم تنفيذ Instructions_départ قبل الدخول في الحلقة لأول مرة. يتم تنفيذ Instructions_fin_boucle بعد كل دورة من دورات الحلقة.

ملاحظات

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

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

instructions_départ
tantque condition
    actions
    instructions_fin_boucle
fintantque

أمثلة

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

            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
يخرج من حلقة for، while، do ... while.
continue
ينتقل إلى التكرار التالي في حلقات for و while و do ... 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

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

  • في الجملة catch، قد لا نرغب في استخدام الكائن Exception المتاح. بدلاً من كتابة 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 التي تُرجع سلسلة أحرف تشير إلى نوع الاستثناء بالإضافة إلى قيمة الخاصية Message. وبذلك يمكننا كتابة:
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) {
            // إعلان وتهيئة مصفوفة
            int[] tab = { 0, 1, 2, 3 };
            int i;
            // عرض المصفوفة باستخدام for
            for (i = 0; i < tab.Length; i++)
                Console.WriteLine("tab[{0}]={1}", i, tab[i]);
            // عرض المصفوفة باستخدام for each
            foreach (int élmt in tab) {
                Console.WriteLine(élmt);
            }
            // توليد استثناء
            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:السطر 7
finally ...
  • السطر 9: حدثت الاستثناء [System.IndexOutOfRangeException]
  • السطر 11: تم تنفيذ الجملة finally (الأسطر 23-25) من الكود، على الرغم من وجود تعليمات return في السطر 21 للخروج من الأسلوب. تجدر الإشارة إلى أن الجملة finally يتم تنفيذها دائمًا.

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


using System;

namespace Chap1 {
    class P08 {
        static void Main(string[] args) {

            // المثال 2
            // يُطلب الاسم
            Console.Write("Nom : ");
            // قراءة الإجابة
            string nom = Console.ReadLine();
            // يُطلب العمر
            int age = 0;
            bool ageOK = false;
            while (!ageOK) {
                // السؤال
                Console.Write("âge : ");
                // قراءة-تحقق من الإجابة
                try {
                    age = int.Parse(Console.ReadLine());
                    ageOK = age>=1;
                } catch {
                }//محاولة-التقاط
                if (!ageOK) {
                    Console.WriteLine("Age incorrect, recommencez...");
                }
            }//while
            // العرض النهائي
            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 الموضوعة في false، السطر 14، ستبقى في 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<=الحقل 1. على سبيل المثال، إذا كان 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) {
            // جداول البيانات اللازمة لحساب الضريبة
            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 };

            // استرداد الحالة الاجتماعية
            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";

            // عدد الأطفال
            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");
                }
            }// بينما

            // الراتب
            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

            // حساب عدد الحصص
            decimal nbParts;
            if (marie) nbParts = (decimal)nbEnfants / 2 + 2;
            else nbParts = (decimal)nbEnfants / 2 + 1;
            if (nbEnfants >= 3) nbParts += 0.5M;

            // الدخل الخاضع للضريبة
            decimal revenu = 0.72M * salaire;

            // الحصة العائلية
            decimal QF = revenu / nbParts;

            // البحث عن شريحة الضرائب المطابقة لـ QF
            int i;
            int nbTranches = limites.Length;
            limites[nbTranches - 1] = QF;
            i = 0;
            while (QF > limites[i]) i++;
            // الضريبة
            int impots = (int)(coeffR[i] * revenu - coeffN[i] * nbParts);

            // عرض النتيجة
            Console.WriteLine("Impôt à payer : {0} euros", impots);
        }
    }
}
  • الأسطر 7-9: تُلحق القيم الرقمية باللاحقة M (Money) لتكون من النوع decimal.
  • السطر 16:
    • تقوم الدالة Console.ReadLine() بتحويل السلسلة C1 التي تم كتابتها على لوحة المفاتيح
    • C1.Trim() تزيل المسافات في بداية ونهاية C1 - وتُرجع سلسلة C2
    • C2.ToLower() تُرجع السلسلة C3 وهي السلسلة C2 بعد تحويلها إلى أحرف صغيرة.
  • السطر 21: يتلقى القيمة المنطقية marie القيمة true أو false من العلاقة reponse=="o"
  • السطر 29: يتم تحويل السلسلة المكتوبة على لوحة المفاتيح إلى النوع int. إذا فشل التحويل، يتم إصدار استثناء.
  • السطر 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]="arg0args[1]="arg1" … وإليك مثال:


using System;

namespace Chap1 {
    class P10 {
        static void Main(string[] args) {
            // يتم سرد المعلمات المستلمة
            Console.WriteLine("Il y a  " + args.Length + " arguments");
            for (int i = 0; i < args.Length; i++) {
                Console.Out.WriteLine("arguments[" + i + "]=" + args[i]);
            }
        }
    }
}

لنقل الحجج إلى الكود المنفذ، سنقوم بما يلي:

  • في [1]: انقر بزر الماوس الأيمن على المشروع / Properties
  • في [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. التعدادات

التعداد هو نوع من أنواع البيانات يكون مجال قيمه مجموعة من الثوابت الصحيحة. لنفترض أن هناك برنامجًا عليه إدارة الإشارات في امتحان. قد يكون هناك خمسة منها: Passable، AssezBien، Bien، TrèsBien، Excellent.

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


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

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


            // متغير يأخذ قيمه من قائمة الإشارات
Mentions maMention = Mentions.Passable;

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


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

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


            // قائمة الإشارات في شكل سلاسل
            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.

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


            //قائمة الإشارات في شكل أعداد صحيحة
            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) {
            // متغير يأخذ قيمه من قائمة Mentions
            Mentions maMention = Mentions.Passable;
            // عرض قيمة المتغير
            Console.WriteLine("mention=" + maMention);
            // اختبار بقيمة من قائمة التعداد
            if (maMention == Mentions.Passable) {
                Console.WriteLine("Peut mieux faire");
            }
            // قائمة الإشارات في شكل سلاسل
            foreach (Mentions m in Enum.GetValues(maMention.GetType())) {
                Console.WriteLine(m);
            }
            //قائمة الإشارات في شكل أعداد صحيحة
            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() {
            // مثال 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() {
            // مثال 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() {
            // مثال 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