في المقال ده، هنفهم بالتفصيل إيه هي الـ Methods أو الـ Functions في لغة C#، وأنواعها، وإزاي بنعملها ونستخدمها في الكود بتاعنا.

What is a Function?

الـ Function ببساطة هي عبارة عن “بلوك” من الكود جواه مجموعة أوامر ليها علاقة ببعض، بتنفذ مهمة معينة ومش بتشتغل غير لما نستدعيها. المهمة دي ممكن تكون صغيرة أو كبيرة، لكن الـ Function بتنفذها بالكامل.

الـ Functions بتقدر تاخد مُدخلات في صورة parameters وبترجع الناتج في صورة return value.

الميزة الكبيرة هنا هي إننا بنكتب الـ Function مرة واحدة، ونقدر نعيد استخدامها أكتر من مرة في أي مكان في البرنامج. ده بيساعدنا نتجنب تكرار الكود وده تطبيق لمبدأ DRY - Don’t Repeat Yourself.

Why Do We Need Functions?

علشان نفهم أهمية الـ Functions (اللي أحيانًا بيتسموا Modules أو Procedures)، تخيل إنك بتكتب برنامج كامل في مكان واحد، يعني كل الأوامر جوه الـ Main function. ده بيخلي إدارته صعبة جدًا.

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

لو كتبنا كل حاجة جوه الـ Main function، الطريقة دي في البرمجة اسمها Monolithic Programming. لو الـ Main بتاعتك فيها آلاف السطور، إدارتها هتبقى شبه مستحيلة ودي مش طريقة كويسة في البرمجة.

Problems in Monolithic Programming

الطريقة دي ليها مشاكل كتير، منها:

  • صعوبة تصحيح الأخطاء: لو حصل error في سطر واحد، ده بيعتبر error في البرنامج كله.
  • صعوبة الإدارة: لو عندك 10,000 سطر كود، مستحيل تخلصهم في يوم واحد. وهتحتاج تفتكر كل تفصيلة عشان تعدل أو تضيف سطر جديد.
  • صعوبة العمل الجماعي: كام واحد يقدر يشتغل على نفس الـ Main function؟ شخص واحد بس. مستحيل الشغل يتقسم على فريق.
  • مشاكل في الـ Memory: لما البرنامج بيكبر أوي، حجمه ممكن يكون مناسب لأجهزة معينة ومش مناسب لأجهزة تانية.

عشان كده، بنفضل نقسم البرنامج لأجزاء صغيرة، قابلة للإدارة، وقابلة لإعادة الاستخدام. الطريقة دي اسمها Modular Programming أو Procedural Programming، وهي الأفضل في تطوير البرامج.

graph TD
    subgraph Monolithic Programming
        A[Main Function - 10,000 Lines of Code]
    end

    subgraph Modular Programming
        M[Main Function] --> F1[Function 1: Task A]
        M --> F2[Function 2: Task B]
        M --> F3[Function 3: Task C]
    end

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

How to Create a Function?

عشان تكتب function في C#، لازم تتبع التركيب ده:

[Access Specifier] [Modifier] [Return Type] [Function Name]([Parameter List])
{
    // Function Body
    return value; // Optional, depends on the return type
}
  • الـ Access Specifier (اختياري): بيحدد مين يقدر يوصل للـ method دي، زي public أو private.
  • الـ Modifier (اختياري): بيحدد نوع الوصول للـ method، زي static. لو استخدمت static، تقدر توصل للـ method مباشرة من غير ما تعمل instance من الـ class.
  • الـ Return Type (إجباري): بيحدد نوع القيمة اللي الـ method هترجعها. لو مش بترجع حاجة، بيكون void.
  • الـ Function Name (إجباري): ده اسم الـ method اللي بنستخدمه عشان نستدعيها.
  • الـ Parameter List (اختياري): دي قائمة الـ parameters اللي الـ function بتاخدها. ممكن متاخدش أي parameters.
  • الـ Function Body: ده الكود أو الأوامر اللي هتتنفذ لما الـ function تتنادى. بيكون بين الأقواس {}.

Function Signature

في C#، الـ Method Signature بيتكون من حاجتين بس:

  1. الـ Method Name
  2. الـ Parameter List (عددهم ونوعهم وترتيبهم)

الـ return type مش بيعتبر جزء من الـ signature.

Types of Functions

فيه نوعين أساسيين من الـ Functions في C#:

  1. Built-in Functions: دي functions جاهزة ومتعرفة في الـ framework عشان المطور يستخدمها على طول.
  2. User-Defined Functions: دي الـ functions اللي المطور بيكتبها بنفسه.

Built-in Functions

الـ Built-in functions بتكون موثوقة جدًا لأنها اتجربت كتير، وبتدي أداء عالي، وبتوفر وقت كبير لأنك مش محتاج تكتب وظايف عامة من الصفر زي الطباعة على الشاشة أو حساب الجذر التربيعي.

مثال:

using System;
 
class Program
{
    static void Main(string[] args)
    {
        int number = 25;
        // Get the square root using a built-in function from the Math class
        double squareRoot = Math.Sqrt(number);
 
        // Use the WriteLine built-in function to print the output
        Console.WriteLine($"Square Root of {number} is {squareRoot}");
    }
}

User-Defined Functions

دي الـ functions اللي إحنا بنكتبها عشان تنفذ مهام خاصة بمشروعنا. بتساعدنا نقلل تعقيد البرنامج ونحسن جودة الكود. إنت كمبرمج عندك تحكم كامل فيها وتقدر تعدلها في أي وقت.

فيه 4 أنواع أساسية من الـ User-Defined Functions:

1. Functions with No Argument and No Return Type

النوع ده من الـ functions مش بيستقبل أي بيانات (arguments) من الـ calling function (الدالة اللي بتستدعيه)، وفي نفس الوقت مش بيرجع أي قيمة ليها. يعني مفيش أي تبادل بيانات بين الدالتين.

الـ function اللي مش بترجع قيمة بيكون الـ return type بتاعها void، ومينفعش نستخدمها جوه expression، لكن بنستدعيها كـ statement مستقل.

مثال:

في الكود ده، الـ function اللي اسمها Sum مش بتاخد أي parameters ومش بترجع قيمة (void).

using System;
namespace FunctionDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Call the function
            Sum();
            Console.ReadKey();
        }
        static void Sum()
        {
            int x = 10;
            int y = 20;
            int sum = x + y;
            Console.WriteLine($"Sum of {x} and {y} is {sum}");
        }
    }
}

الناتج:

Sum of 10 and 20 is 30

2. Functions with No Argument but with Return Type

هنا الـ function مش بتستقبل أي arguments، لكن بترجع قيمة للـ calling function. يعني تبادل البيانات بيحصل في اتجاه واحد بس، من الـ called function للـ calling function.

الـ function بتفضل تتنفذ سطر بسطر لحد ما توصل لـ return statement، وساعتها بترجع القيمة.

مثال:

هنا الأقواس الفاضية في int Result = Sum(); معناها إن مفيش arguments بتتبعت. والقيمة اللي هترجع من Sum() هتتخزن في المتغير Result.

using System;
namespace FunctionDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Call the function and store its return value
            int Result = Sum();
            Console.WriteLine($"Sum is {Result}");
            Console.ReadKey();
        }
        static int Sum()
        {
            int x = 10;
            int y = 20;
            int sum = x + y;
            // Return the calculated sum
            return sum;
        }
    }
}

الناتج:

Sum is 30

3. Functions with Argument but no Return Type

النوع ده من الـ functions بيستقبل بيانات من الـ calling function لكن مش بيرجع ليها أي قيمة. يعني تبادل البيانات بيحصل من الـ calling function للـ called function بس.

مثال:

هنا بنبعت قيمتين (x و y) للـ Sum function، لكنها مش بترجع أي حاجة للـ Main function.

using System;
namespace FunctionDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            int x = 10, y = 20;
            // Call the function and pass arguments
            Sum(x, y);
            Console.ReadKey();
        }
        // Function receives two integer parameters
        static void Sum(int x, int y)
        {
            int sum = x + y;
            Console.WriteLine($"Sum is {sum}");
        }
    }
}

الناتج:

Sum is 30

4. Functions with Argument and with Return Value

ده النوع المثالي من الـ functions اللي بيشتغل زي “صندوق أسود” (Black Box)، بيستقبل مُدخلات وبيطلع مُخرجات. هنا بيحصل تبادل بيانات في الاتجاهين.

مثال:

الـ function هنا بتاخد قيمتين، تحسب مجموعهم، وترجع النتيجة.

using System;
namespace FunctionDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            int x = 10, y = 20;
            // Call the function, pass arguments, and get the return value
            int Result = Sum(x, y);
            Console.WriteLine($"Sum is {Result}");
            Console.ReadKey();
        }
        static int Sum(int x, int y)
        {
            int sum = x + y;
            return sum;
        }
    }
}

الناتج:

Sum is 30

How to Call a Function

لما بنستدعي method، إحنا بنطلب منها تنفذ الأوامر اللي جواها. الاستدعاء بيحتوي على اسم الـ method وأي parameters هي محتاجاها.

لما الـ method بتتنفذ، التحكم بيتنقل ليها، وبعد ما تخلص، التحكم بيرجع للنقطة اللي بعد الاستدعاء مباشرة.

Function Parameters

  • الـ Parameters اللي بنبعتها للـ function لما نستدعيها (زي x و y في المثال اللي فات) اسمها Actual Parameters.
  • الـ Parameters اللي الـ function بتستقبلها في تعريفها (زي a و b) اسمها Formal Parameters.

Optional Parameters

تقدر تخلي الـ parameter اختياري عن طريق إنك تديله قيمة افتراضية.

void SendMessage(string msg = "Default Message")
{
    Console.WriteLine(msg);
}
 
// How to call it:
SendMessage("Hello"); // Prints "Hello"
SendMessage();        // Prints "Default Message"

Advanced Concepts

Function Overloading

في C#، بنقدر نعمل أكتر من function بنفس الاسم، لكن بقائمة parameters مختلفة. ده اسمه Function Overloading، وهو شكل من أشكال الـ Polymorphism.

الـ C# Compiler بيقدر يفرق بين الـ functions دي بناءً على الـ parameters اللي بتبعتها.

مثال:

لو عندنا function بتجمع رقمين:

static int add(int x, int y) {
    return x + y;
}

وعايزين نعمل function تانية بنفس الاسم بتجمع 3 أرقام:

static int add(int x, int y, int z) {
    return x + y + z;
}

لما نستدعي add(a, b)، الكومبايلر هينادي الـ function الأولى. ولما نستدعي add(a, b, c)، هينادي الـ function التانية.

مزايا الـ Function Overloading:

  • مش بنضطر نخترع أسماء جديدة لكل function بتعمل نفس المهمة الأساسية (زي الجمع).
  • بتسهل كتابة البرامج ومش بتحتاج تفتكر أسماء functions كتير.

How Can Be the Parameters List Different?

قائمة الـ parameters ممكن تكون مختلفة في حاجة من اتنين:

  1. عدد الـ parameters.
  2. نوع البيانات (Data Type) بتاعة الـ parameters.

خلينا نشوف أمثلة للـ signatures بتاعة functions مختلفة:

  • int add(int, int): دي valid. بتاخد 2 int وبترجع int.
  • float add(float, float): دي valid. بتاخد نفس عدد الـ parameters لكن بنوع بيانات مختلف (float).
  • int add(int, int, int): دي valid. بتاخد عدد parameters مختلف.
  • float add(int, int): دي invalid. لأنها ليها نفس الـ signature بتاعة أول function (نفس العدد ونفس النوع). الـ return type مش بيفرق في تحديد الـ signature.

مثال كامل:

using System;
namespace FunctionDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            int a = 10, b = 2, c, d;
            
            // Calls add(int, int)
            c = add(a, b);
            Console.WriteLine($"Sum of {a} and {b} is {c}");
            
            // Calls add(int, int, int)
            d = add(a, b, c);
            Console.WriteLine($"Sum of {a}, {b} and {c} is {d}");
            
            // Calls add(float, float)
            Console.WriteLine($"Sum of 10.5 and 25.6 is {add(10.5f, 25.6f)}");
            Console.ReadKey();
        }
        
        static int add(int x, int y)
        {
            return x + y;
        }
        
        static int add(int x, int y, int z)
        {
            return x + y + z;
        }
        
        static float add(float x, float y)
        {
            return x + y;
        }
    }
}

Recursion

الـ Recursion هو إن الـ function تستدعي نفسها. أهم حاجة في الـ recursion هي تحديد Base Case (حالة التوقف) عشان متدخلش في infinite loop.

// Factorial example using recursion
int Factorial(int n)
{
    // Base case
	if (n == 1)
	{
		return 1;
	}
    // Recursive step
	return n * Factorial(n - 1);
}
 
Console.WriteLine(Factorial(5)); // Output: 120

How It Works in Memory (The Call Stack)

الـ Call Stack هي data structure بتستخدمها الذاكرة عشان تتابع الـ functions اللي تم استدعاؤها. بتشتغل بمبدأ LIFO (Last-In, First-Out)، يعني آخر function دخلت هي أول واحدة تخرج.

لما البرنامج بيبدأ والـ Main method تشتغل، بيتعملها مكان في الـ stack memory. لما Main بتستدعي function تانية، بيتعمل frame جديد للـ function دي فوق بتاع Main في الـ stack. لما الـ function تخلص شغلها، الـ frame بتاعها بيتشال من الـ stack والتحكم بيرجع للـ function اللي تحتها.

sequenceDiagram
    participant Main
    participant Add

    note over Main,Add: Program starts, Main() is pushed to Call Stack.
    
    Main->>Add: Call Add(10, 20)
    activate Add
    
    note right of Add: Add() is pushed to Call Stack.
    
    Add-->>Add: Internal calculation: result = 30
    
    Add-->>Main: return 30
    deactivate Add
    
    note right of Main: Add() is popped from Call Stack.
    note over Main: Stores result in 'Result' variable.

لو الـ stack اتملت (وده بيحصل لو عملت function بتستدعي نفسها من غير base case مثلًا)، البرنامج بيحصل فيه error اسمه Stack Overflow وبيفصل.

Best Practices for Writing Functions

  • اسم الـ function يدل على شغلها: CalculateSalary() أحسن من DoWork().

  • مهمة واحدة لكل function: متخليش الـ function تعمل أكتر من حاجة (مبدأ Single Responsibility).

  • متكررش نفسك (DRY): لو لقيت كود مكتوب أكتر من مرة، حوله لـ function.

  • استخدم XML Comments: عشان توضح الـ function دي بتعمل إيه، بتاخد إيه، وبترجع إيه.

    /// <summary>
    /// Calculates the sum of two integers.
    /// </summary>
    /// <param name="a">The first integer.</param>
    /// <param name="b">The second integer.</param>
    /// <returns>The sum of the two integers.</returns>
    public int Sum(int a, int b)
    {
        return a + b;
    }

Suggested Title: A Deep Dive into C# Functions and Methods