تعلم كيفية البرمجة ب #C


الدرس: الوظائف/ الدوال


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

إنشاء وظيفة


الهدف من هذه الوظيفة هو معالجة الكود من أجل تجنب الاضطرار إلى تكرار نفس الكود مرارًا وتكرارًا لسببين رئيسيين:
  • بالفعل لأن الرجل كائن كسول يستخدم ذكائه لتجنب أي عمل غير ضروري.
  • ثم لأنه إذا كان هناك أي شيء يجب تصحيحه في هذا الكود وإذا تم تكراره في عدة أماكن ، فسوف يتعين علينا إجراء تصحيح في كل هذه الأماكن. إذا تم أخذ الكود في الحسبان في مكان واحد ، فسنقوم بتصحيح واحد .  (نعم نعم ، لا يزال الكسل ولكن هذا يسمح أيضًا بتجنب نسيان جزء من الكود في مكان خفي) .
يُعرف هذا الاهتمام بالتعامل مع مبدأ " DRY " والذي هو اختصار للكلمات الإنجليزية "Don’t Repeat Yourself " ، وهذا يعني بالطبع: "لا تكرر نفسك" . الهدف من هذا المبدأ هو ألا تضطر أبدًا (مع استثناءات قليلة بالطبع ...) إلى إعادة كتابة نفس السطر من التعليمات البرمجية.
على سبيل المثال ، دعونا نتخيل بعض الإرشادات التي تهتم بكتابة رسالة ترحيب باسم المستخدم . كود C# يمكن أن يكون:

Console.WriteLine("Bonjour Nicolas");
Console.WriteLine("-------" + Environment.NewLine);
Console.WriteLine("\tBienvenue dans le monde merveilleux du C#"); 
ملاحظة: في التعليمات Console.WriteLine التي نستخدمها بانتظام ، WriteLine هي وظيفة.
إذا أردت لاحقًا إعادة عرض رسالة الترحيب ، فسيتعين عليك إعادة كتابة هذه الأسطر الأربعة من التعليمات البرمجية. ما لم نستخدم وظيفة:

static void AffichageBienvenue()
{
    Console.WriteLine("Bonjour Nicolas");
    Console.WriteLine("-------" + Environment.NewLine);
    Console.WriteLine("\tBienvenue dans le monde merveilleux du C#");
} 
في المثال السابق ، أقوم بتعريف وظيفة تسمى AffichageBienvenue .
التعليمات:

static void AffichageBienvenue()
ويسمى توقيع الوظيفة. إنه يخبرنا عن مُدخلات الوظيفة وما ستعود إليه.
الكلمة الأساسية void تعني أن الوظيفة لا يعرض شيئًا. تشير الأقواس الفارغة في نهاية التوقيع إلى أن الوظيفة لا تحتوي على مُدخلات.
هذا هو أبسط شكل من أشكال الوظيفة الممكنة.
لا تهمنا الكلمة الرئيسية Static في الوقت الحالي ، ولكن كن على علم أنه من المفيد الإشارة إلى أن الوظيفة متاحة دائمًا وجاهزة للاستخدام. في هذا السياق ، هو إلزامي. سوف نعود إليها.
أدناه توقيع الوظيفة ، نجد الأقواس. أنها تجعل من الممكن لتحديد الوظيفة. تشكل كتلة الكود المشكَّلة على هذا النحو ما يسمى "نص الوظيفة" .
باختصار ، لإعلان الوظيفة ، سيكون لدينا:

توقيع الوظيفة 
{
    نص الوظيفة
} 
يمكننا الآن استدعاء (أي ، تنفيذ) هذه الوظيفة في برنامجنا باسمها. على سبيل المثال ، هنا أسميها بسهولة جدًا مرتين على التوالي:

static void Main(string[] args)
{
    AffichageBienvenue();
    AffichageBienvenue();
}

static void AffichageBienvenue()
{
    Console.WriteLine("Bonjour Nicolas");
    Console.WriteLine("-------" + Environment.NewLine);
    Console.WriteLine("\tBienvenue dans le monde merveilleux du C#");
} 
وكل ذلك ، دون جهد! ما زال أبسط وأكثر وضوحًا ، أليس كذلك؟

()Main وظيفة خاصة


هل تذكرك بتوقيع الوظيفة التي أنشأناها للتو؟ لكن نعم ، الكتلة الأخرى فوق أسلوبنا ، والتي تبدو أيضًا كوظيفة.
هذه وظيفة خاصة ، الوظيفة Main() .
تم إنشاؤها بواسطة Visual Studio Express عندما أنشأنا مشروع وحدة التحكم .
هذه الوظيفة هي في الواقع نقطة دخول التطبيق ، أي عندما يحاول CLR تنفيذ طلبنا ، فإنه يبحث عن هذه الوظيفة ليتمكن من بدء تنفيذ التعليمات منها . إذا لم يجدها ، فلن يتمكن من تشغيل طلبنا. هذا هو السبب في أنه من المهم أن هذه الوظيفة يمكن الوصول إليها من كل مكان ؛ تذكر أنه بفضل الكلمة الرئيسية static ستتاح لنا الفرصة للدراسة بمزيد من التفاصيل لاحقًا.
Visual Studio Express يمنعنا بشكل جيد من هذا الخطأ. في الواقع ، إذا حذفت هذه الوظيفة (أو أزلت الكلمة الرئيسية static ) وحاولت ترجمة طلبنا ، فستتلقى رسالة الخطأ التالية:
Citation : Compilateur
Erreur 1 Le programme 'c:\Users\nico\Documents\Visual Studio 2013\Projects\MaPremiereApplication\MaPremiereApplication\obj\Debug\MaPremiereApplication.exe' ne contient pas une méthode 'Main' statique appropriée pour un point d'entrée
رسالة الخطأ واضحة. إنه يحتاج إلى وظيفة Main() للبدء.
انظر لهذه الوظيفة الخاصة Main() . من الضروري في أي برنامج قابل للتنفيذ وهذا هو المكان الذي يبدأ فيه البرنامج.
سوف يلاحظ القراء المهتمون أن هذه الوظيفة بها أشياء في التوقيع ، بين قوسين ... مُدخلات !  سنكتشفها في الفصل التالي ...

مُدخلات الوظيفة


عظيم ، نحن نعرف كيفية إنشاء وظائف. سنكون قادرين على إنشاء وظيفة تجعل من الممكن الترحيب بالشخص الذي وصل للتو إلى تطبيقنا على سبيل المثال. إذا كان نيكولاس هو الذي وصل للتو ، فسنكون قادرين على استدعاء الوظيفة AffichageBienvenueNicolas() . إذا كان جيريمي ، فسوف نسمي الوظيفة AffichageBienvenueJeremie()، إلخ.

static void AffichageBienvenueNicolas()
{
    Console.WriteLine("Bonjour Nicolas");
    Console.WriteLine("-------" + Environment.NewLine);
    Console.WriteLine("\tBienvenue dans le monde merveilleux du C#");
}

static void AffichageBienvenueJeremie()
{
    Console.WriteLine("Bonjour Jérémie");
    Console.WriteLine("-------" + Environment.NewLine);
    Console.WriteLine("\tBienvenue dans le monde merveilleux du C#");
} 
أخيرًا ، أخيرًا ، ليست كبيرة كما هي. بينما ذكرنا للتو مبدأ DRY ، فقد انتهى الأمر بطريقتين متطابقتين تقريبًا تختلفان فقط في شيء صغير جدًا.
هذا هو المكان الذي تأتي فيه مُدخلات الوظيفة. ذكرناها في الفقرة السابقة ، فمن الممكن تمرير المُدخلات إلى وظيفة. وبالتالي ، سوف نكون قادرين على استخدام قيم هذه المُدخلات في متن أساليبنا ، ستصبح الأساليب عامة بشكل أكبر.
في مثالنا على عرض رسالة الترحيب ، من الواضح أن اسم المستخدم سيكون مُدخلا للوظيفة.
تتم كتابة المُدخلات داخل الأقواس التالية لاسم الوظيفة. يجب أن نشير إلى نوع المُدخل وكذلك اسم المتغير الذي سيمثلها في الوظيفة.
من الممكن تمرير العديد من المُدخلات إلى وظيفة ، وسنفصل بينها بفاصلة. مثلا :

static void DireBonjour(string prenom, int age)
{
    Console.WriteLine("Bonjour " + prenom);
    Console.WriteLine("Vous avez " + age + " ans");
} 
هنا ،  DireBonjour يأخذ الوظيفة كمُدخلات سلسلة أحرف  prenom و عدد صحيح age .
تعرض الوظيفة "Bonjour" وكذلك محتوى المتغير prenom . وبالمثل ، أسفله مباشرة ، يعرض العمر(age) الذي تم تمريره في المُدخلات.
يمكننا أن نسمي هذه الوظيفة بهذه الوظيفة ، من الوظيفة Main() :

static void Main(string[] args)
{
    DireBonjour("Nicolas", 30);
    DireBonjour("Jérémie", 20);
} 
وسيكون لدينا:
c sharp .net framework
بالطبع ، من الضروري توفير متغير من نفس النوع مثل المُدخل في مُدخلات الوظيفة. وإلا ، فلن يتمكن المترجم البرمجي من وضع البيانات التي تم تمريرها في المُدخل. بالإضافة إلى ذلك ، إذا لم تقدم المُدخل الصحيح ، فسينتج لك خطأ في الترجمة البرمجية.
على سبيل المثال ، إذا قمت بالاتصال بالوظيفة باستخدام المُدخلات التالية:

DireBonjour(10, 10); 
سيكون لديك خطأ الترجمة البرمجية التالي:
من المستحيل التحويل من "int" إلى "string"
من الواضح أنه من الممكن تمرير المتغيرات إلى وظيفة ما ، وتعمل بنفس الوظيفة:

string prenom = "Nicolas";
DireBonjour(prenom, 30); 
سنعود بمزيد من التفصيل إلى ما يحدث بالضبط هنا في الفصل الخاص بوضع التمرير ، والذي سنراه في دورة مستقبلية.
كما ترى ، فهي تشبه إلى حد كبير ما قمنا به بالفعل مع هذه الوظيفة Console.WriteLine() . سهل ، أليس كذلك؟
تعد هذه الوظيفة Console.WriteLine جزءًا من مكتبة .NET Framework ويتم استخدامها لكتابة سلاسل أو أرقام أو أشياء أخرى كثيرة على وحدة التحكم. يحتوي .NET Framework على الكثير من وظائف المساعدة من جميع الأنواع ، وسنعود إلى ذلك.
ربما تكون قد لاحظت تفاصيل ، فقد استهلنا جميع أساليبنا بالكلمة الرئيسية static . قلت أنه كان إلزاميا في سياقنا ، أن نكون أكثر دقة ، لأن الوظيفة Main() ثابتة أننا مضطرون إلى إنشاء وظائف ثابتة. قيل إنه يجب أن تكون الوظيفة Main() ثابتة لأنه يجب أن يكون الوصول إليها من أي مكان حتى يتمكن CLR من العثور على نقطة الدخول لبرنامجنا. ومع ذلك ، يمكن لوظيفة ثابتة أن تستدعي فقط الأساليب الثابتة ، وهذا هو السبب في أننا مضطرون (الآن) إلى بادئة وظائفنا بالكلمة الرئيسية static . سنصف بالضبط ما الذي يغطي الكلمة الرئيسية static في القسم التالي.

ارجاع الوظيفة


يمكن للوظيفة أيضًا إرجاع قيمة ، على سبيل المثال عملية حسابية. غالبًا ما تكون فائدتها الأساسية.
يمكننا أن نتخيل على سبيل المثال الوظيفة التي تحسب طول الوتر الذي يبدأ من جانبي المثلث. مع العلم أن a² + b² = c² ، يمكننا أن نتخيل وظيفة تأخذ في المُدخلات طول الجانبين ، وتجمع مجموع المربعات وتعيد الجذر التربيعي للنتيجة. هذا ما تفعله الوظيفة التالية:

static double LongueurHypotenuse(double a, double b)
{
    double sommeDesCarres = a * a + b * b;
    double resultat = Math.Sqrt(sommeDesCarres);
    return resultat;
} 
دعنا نواصل تجاهل الكلمة static . ستلاحظ أن توقيع الوظيفة يبدأ بالكلمة الرئيسية double، مما يشير إلى أن الوظيفة ستُرجع قيمة النوع المزدوج. كما رأينا، double a و double b هما مُدخلات الوظيفة وهي من نوع double .
الوظيفة Math.Sqrt عبارة عن وظيفة لإطار عمل .NET ، تمامًا مثل الوظيفة Console.WriteLine التي تُرجع الجذر التربيعي للرقم . تأخذ كمُدخل double و تُرجع كنتيجة double   أيضًا التي تتوافق مع الجذر التربيعي للمثدخل. من الطبيعي أن نخزن هذه النتيجة في متغير بفضل عامل التشغيل "=" .
في نهاية الوظيفة ، return تشير الكلمة الأساسية إلى أن الوظيفة تُرجع القيمة إلى الوظيفة التي تسمىها. هنا نعيد النتيجة.
يمكن استخدام هذه الوظيفة على النحو التالي:

static void Main(string[] args)
{
    double valeur = LongueurHypotenuse(1, 3);
    Console.WriteLine(valeur);
    valeur = LongueurHypotenuse(10, 10);
    Console.WriteLine(valeur);
} 
كما كان من قبل ، نستخدم متغيرًا لتخزين نتيجة تنفيذ الوظيفة.
والتي سوف ينتج:
c sharp .net framework
لاحظ أنه من الممكن أيضًا الاستغناء عن متغير وسيط لتخزين النتيجة. لذلك ، على سبيل المثال ، يمكننا القيام بما يلي:

Console.WriteLine("Le résultat est : " + LongueurHypotenuse(1, 3)); 
عند كتابة هذه السطور ، يتم توصيل النتيجة التي يتم إرجاعها بواسطة الوظيفة LongueurHypotenuse مباشرةً إلى السلسلة " Le résultat est :  " ويتم تمريرها كمُدخل إلى الوظيفة Console.WriteLine .
لاحظ أننا قمنا بالعملية a * a لكي نحسب مربع a . كان بإمكاننا أيضًا القيام بهذا Math.Pow(a, 2) مما يسمح لنا بالقيام بنفس الشيء ، والفرق هو أنه Pow تسمح لنا بوضع القوة التي نريدها. لذلك Math.Pow(a, 3) تمككننا من حساب "a" مكعب.
انتبه إلى أن الكلمة الرئيسية return يمكن أن تظهر في أي مكان في هذه الوظيفة. ثم يقطع تنفيذ الكود ويعيد القيمة التي تم تمريرها. هذه الكلمة الأساسية إلزامية ، وإلا لن يتم تجميع الوظيفة.
من الضروري أيضًا أن تعيد جميع المسارات الممكنة للأسلوب شيئًا ما. يتم تحديد المسارات حسب التعليمات الشرطية التي رأيناها سابقًا.
إذن المثال التالي صحيح:

static string Conjugaison(string genre)
{
    if (genre == "homme")
        return "é";
    else
        return "ée";
} 
لأنه بغض النظر عن قيمة المتغير "genre" ، فإن الوظيفة ستُرجع السلسلة.
بينما هذا واحد:

static string Conjugaison(string genre)
{
    if (genre == "homme")
        return "é";
    else
    {
        if (genre == "femme")
            return "ée";
    }
} 
غير صحيح في الواقع ، ما هي الوظيفة التي تعود بها إذا كان متغير "genre" يحتوي على شيء آخر غير homme أو femme؟
بشكل عام ، سيعلمنا Visual Studio Express بأنه يكتشف مشكلة في خطأ الترجمة.
يمكننا تصحيح هذا باستخدام على سبيل المثال:

static string Conjugaison(string genre)
{
    if (genre == "homme")
        return "é";
    else
    {
        if (genre == "femme")
            return "ée";
    }
    return "";
} 
لاحظ أن "" يتوافق مع سلسلة فارغة، ويمكن أيضا أن تكون مكتوبة string.Empty .
لقد رأينا في الفصل السابق أنه من الممكن إنشاء وظائف لا تُرجع أي شيء. في هذه الحالة ، يمكن استخدام الكلمة الأساسية return بدون قيمة تتبعها لإيقاف تنفيذ الوظيفة .  مثلا :

static void Bonjour(string prenom)
{
    if (prenom == "inconnu")
        return;
    Console.WriteLine("Bonjour " + prenom);
} 
وبالتالي ، إذا كان المتغير "prenom" يساوي "inconnu" ، فإننا نترك الوظيفة Bonjour ولن يتم تنفيذ  التعليمة Console.WriteLine .

في الخلاصة


  • تجمع الوظيفة معًا مجموعة من الإرشادات التي يمكنها أخذ المُدخلات والتي يمكنها إرجاع قيمة.
  • يجب استخدام مُدخلات الوظيفة بالنوع الصحيح.
  • البادئة void هي الوظيفة التي لا تُرجع شيئًا  .
  • نقطة دخول البرنامج هي الوظيفة الثابتة Main() .
  • يتم  استخدام الكلمة الأساسية returnلإرجاع قيمة نوع الإرجاع للوظيفة ، إلى من استدعى هذه الوظيفة.