Menu
flag Ar iconالعربيةdown icon

الدوال في C#: المعاملات والقيم المعادة وparams والتحميل الزائد وMain

كيف تصرّح بالدوال وتستدعيها في C#: أنواع الإرجاع وvoid، والمعاملات، والدوال بجسم تعبيري، وإعادة عدة قيم بصف، والتحميل الزائد، ومصفوفات params، والدوال الساكنة مقابل دوال المثيل، والدالة Main، والاستدعاء الذاتي، والدوال المحلية.

تحتوي هذه الصفحة على محررات قابلة للتشغيل - حرّر، شغّل، وشاهد النتيجة فوراً.

الدالة (method) كتلة شيفرة مسمّاة تأخذ مدخلات (معاملات)، وتؤدي عملها، ويمكنها إعادة نتيجة (القيمة المعادة). في C# تعيش كل دالة داخل نوع؛ لا توجد دوال قائمة بذاتها.

المخرجات:

Headphones   48.00
Cable        11.40

تشريح الدالة

public static decimal PriceWithTax(decimal net, decimal taxRate)
{
    return net * (1 + taxRate);
}
  • معدّل الوصول (public وprivate و...): من يُسمح له باستدعائها. إن حُذف تكون الدالة private لنوعها.
  • static: تنتمي الدالة إلى النوع لا إلى كائن. المزيد عن ذلك أدناه.
  • نوع الإرجاع: نوع القيمة التي تعيدها، أو void حين لا تعيد شيئًا.
  • الاسم: PascalCase عُرفًا، وعادة فعل (Calculate وSendEmail وTryParse).
  • قائمة المعاملات: صفر أو أكثر من أزواج type name. كل معامل متغيّر محلي يُهيّأ من وسيط المستدعي.
  • الجسم: العبارات بين القوسين المعقوصين.

الاسم مع أنواع المعاملات (وأي معدّلات ref أو out أو in) يكوّن توقيع الدالة. نوع الإرجاع ليس جزءًا منه، وهذا مهم للتحميل الزائد.

القيم المعادة وvoid

تنهي return الدالة فورًا وتعيد قيمة. يجب على الدالة ذات نوع الإرجاع أن تعيد قيمة من ذلك النوع في كل مسار فيها:

static string Grade(int score)
{
    if (score >= 50)
        return "pass";
}   // error CS0161: 'Program.Grade(int)': not all code paths return a value

يفحص المترجم كل فرع. أضف return "fail"; الناقصة، أو throw إن كان بلوغ تلك النقطة خطأً برمجيًا.

الدالة void لا تعيد شيئًا. لكنها ما زالت تستطيع استخدام return; مجردة للمغادرة مبكرًا:

المخرجات:

ana@mail.com: newsletter sent
ben@mail.com: not subscribed, skipped
False
True

الدوال بجسم تعبيري

حين يكون جسم الدالة تعبيرًا واحدًا تحلّ => محل الأقواس المعقوصة وreturn:

static decimal Discount(decimal price) => price * 0.1m;
static bool IsAdult(int age) => age >= 18;
static void Log(string message) => Console.WriteLine($"[log] {message}");

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

المعاملات نُسخ

تُمرَّر الوسائط بالقيمة افتراضيًا: يستقبل المعامل نسخة من الوسيط. الإسناد إلى المعامل داخل الدالة لا يؤثر في متغيّر المستدعي:

المخرجات:

inside: 42
after:  21
scores: 70, 85, 100

تغيّرت List لأن القيمة المنسوخة في النوع المرجعي هي المرجع: تشير الدالة والمستدعي إلى كائن القائمة نفسه. إعادة الإسناد scores = new List<int>() داخل الدالة لن تؤثر في المستدعي. ولتسمح لدالة بالإسناد إلى متغيّر المستدعي استخدم ref أو out؛ ولإعطاء المعاملات قيمًا افتراضية واستدعائها بالاسم استخدم المعاملات الاختيارية والوسائط المسمّاة.

إعادة عدة قيم بصف

منذ C# 7.0 يمكن للدالة إعادة صف بعناصر مسمّاة، ويمكن للمستدعي تفكيكه إلى متغيّرات منفصلة:

المخرجات:

min 15, max 27, avg 21.0
range 12

يُهمل _ العنصر الذي لا تحتاجه. وللنتائج المستخدمة في أماكن كثيرة يوثّق صنف صغير أو سجل بخصائص مسمّاة الشكل أفضل من الصف.

التحميل الزائد

يمكن لعدة دوال أن تتشارك اسمًا إن اختلفت قوائم معاملاتها في العدد أو الأنواع أو ترتيب الأنواع. يختار المترجم أفضل تطابق لكل استدعاء:

المخرجات:

12.57
13.50
25

تختار Area(5) نسخة int لأنها تطابق تام؛ وتختار Area(2.0) نسخة double. لا يمكن أن تختلف الدوال فقط في نوع الإرجاع: دالتا Area(int) تعيدان int وdouble هما الخطأ CS0111، Type 'Program' already defines a member called 'Area' with the same parameter types، لأن استدعاءً مثل Area(5) لن يستطيع التمييز بينهما.

والاستدعاء الذي لا يقبله أي شكل يفشل وقت الترجمة أيضًا: Area(1, 2, 3) هو CS1501، No overload for method 'Area' takes 3 arguments، وArea("big") هو CS1503، Argument 1: cannot convert from 'string' to 'double'.

params: عدد متغيّر من الوسائط

يقبل معامل مصفوفة params أي عدد من الوسائط، بما في ذلك لا شيء. يبني المترجم المصفوفة عنك:

المخرجات:

empty cart: 0 item(s), 0
one item: 1 item(s), 9.99
three items: 3 item(s), 19.75
from array: 2 item(s), 50

القواعد: يجب أن يكون params المعامل الأخير (وإلا CS0231)، وللدالة واحد على الأكثر، وتمرير مصفوفة موجودة يسلّم تلك المصفوفة كما هي. الدالتان Console.WriteLine("{0} and {1}", a, b) وstring.Format من نوع params object[]. وسّعت C# 13 params لتشمل أنواع مجموعات أخرى مثل List<T> وReadOnlySpan<T>؛ وقبل ذلك لم يُسمح إلا بالمصفوفات.

الدوال الساكنة ودوال المثيل

تنتمي الدالة static إلى النوع وتُستدعى على اسم النوع: Math.Max(3, 7) وProgram.Total(...). أما دالة المثيل فتنتمي إلى كائن ويمكنها استخدام حقوله:

المخرجات:

12

استدعاء دالة مثيل من دالة ساكنة دون كائن هو أشيع خطأ للمبتدئين في C#:

class Program
{
    void Greet() => Console.WriteLine("hi");

    static void Main()
    {
        Greet();
        // error CS0120: An object reference is required for the non-static field, method, or property 'Program.Greet()'
    }
}

علّم Greet بـ static، أو أنشئ كائنًا واستدعها عليه: new Program().Greet();. تغطي صفحة static الأعضاء الساكنة والأصناف الساكنة بعمق أكبر.

الدالة Main

Main هي حيث يبدأ البرنامج. يجب أن تكون static، وهذه التواقيع صالحة:

static void Main() { }
static void Main(string[] args) { }            // command-line arguments
static int Main(string[] args) { return 0; }   // exit code for the shell
static async Task Main() { }                    // C# 7.1 and later

يتيح الشكل async Task Main لنقطة الدخول استخدام await مباشرة (راجع async وawait). ومنذ C# 9 يمكن للملف بدلًا من ذلك أن يحتوي على عبارات عالية المستوى: شيفرة بلا صنف ولا Main إطلاقًا، كما في مشروع dotnet new console جديد. يولّد المترجم الصنف والدالة Main حولها، فهي الشيء نفسه بشكل أقصر؛ وما زالت args متاحة.

الاستدعاء الذاتي

يمكن للدالة أن تستدعي نفسها. يحصل كل استدعاء على معاملاته ومتغيّراته المحلية الخاصة، ويجب أن توقف حالة أساس السلسلة:

المخرجات:

120
2432902008176640000
35

دون حالة أساس، أو مع مدخلات عميقة جدًا، يضيف كل استدعاء إطارًا إلى المكدّس حتى يموت البرنامج بـ StackOverflowException. لا يمكن التقاط هذا الاستثناء، فللمدخلات مجهولة العمق أعد كتابة الاستدعاء الذاتي كحلقة.

الدوال المحلية

قدّمت C# 7.0 الدوال المحلية: دوال مصرّح بها داخل دالة أخرى، ومرئية هناك فقط. يمكنها استخدام متغيّرات الدالة المحيطة:

// C# 7.0 and later
static int CountValid(string[] codes)
{
    int count = 0;
    foreach (string code in codes)
    {
        if (IsValid(code)) count++;
    }
    return count;

    bool IsValid(string c) => c.Length == 6 && char.IsLetter(c[0]);
}

تبقي الدالة المساعدة بجانب مستدعيها الوحيد دون كشفها لبقية الصنف. ومنذ C# 8 يمكن تعليمها بـ static لمنع التقاط المتغيّرات الخارجية. وحيث لا تتوفر دالة محلية تؤدي دالة ساكنة خاصة في الصنف نفسه المهمة نفسها، كما في الأمثلة الأخرى في هذه الصفحة.

الأسئلة الشائعة

ما الفرق بين method وfunction في C#؟

في C# تُصرَّح كل دالة داخل صنف أو بنية أو سجل، وتُسمّى method. لا توجد دوال قائمة بذاتها. حتى العبارات عالية المستوى (C# 9) والدوال المحلية (C# 7) تُترجم إلى methods في نوع مولَّد أو محيط. تُستخدم كلمة "function" بشكل غير رسمي، ولتعابير lambda والمفوّضات.

ماذا تفعل params في C#؟

تتيح params لدالة أن تأخذ عددًا متغيّرًا من الوسائط من نوع واحد: يمكن استدعاء static int Sum(params int[] numbers) بالشكل Sum() أو Sum(4) أو Sum(4, 5, 6) أو Sum(existingArray). يجمع المترجم الوسائط في مصفوفة. يجب أن يكون معامل params الأخير، ولا يمكن للدالة أن تملك إلا واحدًا.

ما هي الدالة Main في C#؟

Main نقطة دخول البرنامج: الدالة التي يستدعيها وقت التشغيل أولًا. يجب أن تكون static، ويمكن أن تعيد void أو int (رمز الخروج)، ويمكن أن تأخذ string[] args لوسائط سطر الأوامر. منذ C# 7.1 يمكن أن تكون أيضًا async Task Main، ومنذ C# 9 تتيح العبارات عالية المستوى للمترجم توليدها عنك.

كيف أعيد عدة قيم من دالة في C#؟

أعد صفًا: static (int Min, int Max) Range(int[] values) وreturn (min, max);. يستطيع المستدعي قراءة result.Min أو التفكيك بـ var (lo, hi) = Range(values);. للقيم التي تنتقل معًا في شيفرتك يكون صنف صغير أو سجل أوضح؛ ومعاملات out هي البديل الأقدم.

لماذا أحصل على "An object reference is required for the non-static field, method, or property"؟

استدعت دالة static مثل Main دالة مثيل مباشرة. الدوال الساكنة تنتمي إلى النوع ولا كائن لديها لتستدعيها عليه. إما أن تعلّم الدالة المستدعاة بـ static، أو تنشئ كائنًا أولًا: var app = new Program(); app.Greet();.

ماذا يعني "not all code paths return a value"؟

الخطأ CS0161: لدالة ذات نوع إرجاع مسار واحد على الأقل يصل إلى القوس الختامي دون return. عادة تعيد if قيمة لكن لا توجد else ولا return أخيرة بعدها. أضف return (أو throw) في نهاية الدالة.

Coddy programming languages illustration

تعلّم البرمجة مع Coddy

ابدأ الآن