تخطَّ إلى المحتوى

الدوال في C#‎: Methods والمعاملات المسماة

الدرس 5 من 11 · C#

الدوال تحوّل البرنامج الطويل إلى قطع صغيرة واضحة، كل قطعة لها اسم ووظيفة واحدة. في C#‎ تسمى الدوال Methods، وفيها ميزات مريحة جداً: صيغة مختصرة في سطر واحد، وقيم افتراضية، ومعاملات تُكتب بأسمائها. في هذا الدرس ستكتب دوالك الخاصة وتستخدم كل هذه الميزات.

Greet();
Console.WriteLine(Add(3, 4));
static void Greet()
{
Console.WriteLine("أهلاً بك في C#!");
}
static int Add(int a, int b)
{
return a + b;
}
الناتج
أهلاً بك في C#!
7
  • static int Add(int a, int b): نوع القيمة المرجعة int، ثم الاسم، ثم المعاملات بأنواعها.
  • void: الدالة لا تُرجع شيئاً.
  • return: تُرجع القيمة وتُنهي الدالة.
  • العُرف في C#‎: أسماء الدوال بصيغة PascalCase (كل كلمة تبدأ بحرف كبير) مثل Add و PriceAfterDiscount.

في البرامج ذات «الجمل العلوية» (Top-level Statements) مثل هذا، تكتب الأوامر في أعلى الملف، والدوال في أسفله. وكلمة static تجعل الدالة لا تعتمد على كائن، وستفهمها أكثر في درس الكلاسات.

إذا كانت الدالة تُرجع تعبيراً واحداً:

static int Square(int x) => x * x;
static string MakeTea(int sugar = 2) => $"شاي بـ {sugar} ملاعق سكر";

Square(5) تُرجع 25. والسهم => يساوي { return ...; }. هذه الصيغة تسمى Expression-bodied Members.

القيم الافتراضية والمعاملات المسماة

Section titled “القيم الافتراضية والمعاملات المسماة”
Console.WriteLine(MakeTea());
Console.WriteLine(MakeTea(0));
الناتج
شاي بـ 2 ملاعق سكر
شاي بـ 0 ملاعق سكر

sugar = 2 قيمة افتراضية تُستخدم إذا لم ترسل قيمة. والمعاملات ذات القيم الافتراضية تأتي في آخر القائمة.

وتستطيع عند الاستدعاء كتابة اسم كل معامل، فلا يهم الترتيب:

static string CreateUser(string name, int age, string city = "القاهرة")
{
return $"{name} ({age} سنة) من {city}";
}
Console.WriteLine(CreateUser("عمر", 15));
Console.WriteLine(CreateUser(age: 20, name: "منى", city: "جدة"));
الناتج
عمر (15 سنة) من القاهرة
منى (20 سنة) من جدة

المعاملات المسماة تجعل الاستدعاء مقروءاً مثل الجملة، خاصة عندما تكثر المعاملات أو تتشابه أنواعها، مثل SendEmail(to: ..., subject: ..., body: ...).

static void TryToChange(int x)
{
x = 100;
}
static void AddPoints(ref int score, int bonus)
{
score += bonus;
}
int value = 5;
TryToChange(value);
Console.WriteLine(value);
int score = 10;
AddPoints(ref score, 5);
Console.WriteLine(score);
الناتج
5
15
  • افتراضياً، C#‎ ترسل نسخة من القيمة، فلم تتغير value.
  • مع ref، ترسل المتغير الأصلي نفسه، فتغيرت score. ولاحظ أن ref تُكتب في التعريف وفي الاستدعاء معاً، لتكون واضحة لكل من يقرأ الكود.

تخيّل الفرق: بدون ref تعطي صديقك صورة من ورقتك، ومع ref تعطيه الورقة نفسها.

لكن الأسلوب المفضل غالباً هو أن تُرجع الدالة القيمة الجديدة: score = AddBonus(score, 5);، فهو أوضح، ونستخدم ref فقط عند الحاجة الحقيقية.

الدالة في C#‎ يمكن أن تكون قيمة تخزنها في متغير:

Func<int, int> twice = x => x * 2;
Console.WriteLine(twice(21));
الناتج
42
  • x => x * 2: دالة مجهولة (Lambda): المعامل قبل السهم، والنتيجة بعده.
  • Func<int, int>: نوع الدالة: تأخذ int وتُرجع int. (آخر نوع بين < > هو نوع القيمة المرجعة دائماً.)

ستستخدم الـ Lambda كثيراً مع القوائم في الدرس القادم، مثل: grades.Where(g => g >= 75).

Console.WriteLine(Math.Max(4, 9));
Console.WriteLine(Math.Sqrt(16));
Console.WriteLine(Math.Pow(2, 10));
Console.WriteLine(Math.Round(3.6));
الناتج
9
4
1024
4

لاحظ أن Math.Sqrt(16) نوعها double، لكنها تُطبع 4 لأن C#‎ لا تطبع الأصفار غير الضرورية.

Console.WriteLine(PriceAfterDiscount(200));
Console.WriteLine(PriceAfterDiscount(200, percent: 25));
static double PriceAfterDiscount(double price, double percent = 10) =>
price - price * percent / 100;
الناتج المتوقع
180
150

تحدٍّ: اكتب دالة Grade(int score) بتعبير switch تُرجع «ممتاز» أو «ناجح» أو «راسب»، ثم خزّنها في Func<int, string> واستدعها من المتغير.

  • نسيان return: خطأ not all code paths return a value عندما يوجد طريق في الدالة لا يُرجع قيمة.
  • نسيان ref في الاستدعاء: إذا كان المعامل ref، يجب كتابة ref عند الاستدعاء أيضاً.
  • قيمة افتراضية قبل معامل إجباري: void F(int a = 1, int b) خطأ ترجمة.

سؤال

أي استدعاء صحيح للدالة static void Order(string item, int qty = 1)؟

سؤال

ماذا يعني النوع Func<string, int>؟

  • الدالة: نوع مرجع واسم بصيغة PascalCase ومعاملات، و void لا تُرجع شيئاً.
  • => تختصر الدالة ذات التعبير الواحد.
  • القيم الافتراضية في آخر المعاملات، والمعاملات المسماة تجعل الاستدعاء واضحاً.
  • C#‎ ترسل نسخة افتراضياً، و ref في التعريف والاستدعاء لتعديل الأصل.
  • Lambda مثل x => x * 2، ونوعها Func<المدخل, المخرج>.