في المقال ده، هنفهم بالتفصيل إيه هي الـ 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 سطر كود، مستحيل تخلصهم في يوم واحد. وهتحتاج تفتكر كل تفصيلة عشان تعدل أو تضيف سطر جديد.
- صعوبة العمل الجماعي: كام واحد يقدر يشتغل على نفس الـ
Mainfunction؟ شخص واحد بس. مستحيل الشغل يتقسم على فريق. - مشاكل في الـ 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 بيتكون من حاجتين بس:
- الـ
Method Name - الـ
Parameter List(عددهم ونوعهم وترتيبهم)
الـ return type مش بيعتبر جزء من الـ signature.
Types of Functions
فيه نوعين أساسيين من الـ Functions في C#:
- Built-in Functions: دي
functionsجاهزة ومتعرفة في الـframeworkعشان المطور يستخدمها على طول. - 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 ممكن تكون مختلفة في حاجة من اتنين:
- عدد الـ
parameters. - نوع البيانات (Data Type) بتاعة الـ
parameters.
خلينا نشوف أمثلة للـ signatures بتاعة functions مختلفة:
int add(int, int): ديvalid. بتاخد 2intوبترجع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: 120How 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