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

المعاملات الاختيارية والوسائط المسمّاة في C#: القيم الافتراضية والقواعد

كيف تعمل المعاملات الاختيارية والوسائط المسمّاة في C#: القيم الافتراضية وقاعدة الثابت وقت الترجمة، وترتيب المعاملات، وتخطي الوسائط بالاسم، والمعاملات الاختيارية مقابل التحميل الزائد، وسمات معلومات المستدعي، وفخ الإصدارات بسبب القيم الافتراضية المدمجة في المستدعين.

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

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

المخرجات:

to ana@mail.com: (no subject), retries 3
to ben@mail.com: Invoice #1042, retries 3
to cy@mail.com: (no subject) [URGENT], retries 3
to dev@mail.com: Build failed, retries 0

يتخطى الاستدعاء الثالث subject ويضبط urgent بالاسم؛ ودون الوسائط المسمّاة كان عليه أن يمرّر "(no subject)" مجددًا فقط ليصل إلى الموضع الثالث. ويمرّر الاستدعاء الأخير كل الوسائط بالاسم بترتيب مختلف عن التصريح، وهذا مسموح.

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

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

  • ثابت (3 و"INFO" وtrue و1.5m وحقل const وعضو تعداد)
  • null لنوع مرجعي أو نوع قابل لـ null
  • default(T)، أو new T() لنوع قيمة T

أي شيء يُقيَّم وقت التشغيل مرفوض. الحالة التقليدية هي التاريخ:

static void Schedule(string task, DateTime at = DateTime.Now) { }
// error CS1736: Default parameter value for 'at' must be a compile-time constant

الحل الالتفافي القياسي معامل قابل لـ null قيمته الافتراضية null، يُحسم في الجسم:

المخرجات:

backup at 2026-01-01 09:00, tags: 0
report at 2026-03-15 18:30, tags: 0
deploy at 2026-01-01 09:00, tags: 2

الحيلة نفسها تغطي المجموعات: القيمة الافتراضية new List<string>() ليست ثابتًا، فاجعل الافتراضي null وأنشئ القائمة في الداخل. وهذا يتجنّب أيضًا خطأ القيمة الافتراضية المشتركة القابلة للتغيير المعروف في Python، الذي تمنعه C# بالسماح بالثوابت فقط.

قواعد الترتيب

تأتي المعاملات الإلزامية أولًا، ثم الاختيارية، ثم مصفوفة params (إن وُجدت) أخيرًا:

static void Log(string message, string level = "INFO", params string[] tags) { }   // OK

static void Log(string level = "INFO", string message) { }
// error CS1737: Optional parameters must appear after all required parameters

لا يمكن أن تكون معاملات ref وout اختيارية.

من جهة الاستدعاء تملأ الوسائط الموضعية المعاملات من البداية. ويمكن أن تليها الوسائط المسمّاة بأي ترتيب. منذ C# 7.2 يمكن للوسيط المسمّى أن يظهر أيضًا قبل وسيط موضعي، لكن فقط حين يكون في موضعه نفسه (SendEmail("a@b.c", subject: "Hi", true))؛ وفي الإصدارات الأقدم يجب أن تأتي الوسائط المسمّاة كلها في النهاية. وحذف معامل إلزامي، حتى مع تسمية غيره، خطأ ترجمة.

الوسائط المسمّاة لسهولة القراءة

الوسائط المسمّاة مفيدة حتى حين لا يكون شيء اختياريًا. القيم الحرفية true وfalse وnull والأرقام المجردة لا تقول شيئًا في موضع الاستدعاء:

ResizeImage(photo, 800, 600, true, false);                                   // which is which?
ResizeImage(photo, width: 800, height: 600, keepAspect: true, upscale: false);

تصبح إعادة تسمية معامل تغييرًا كاسرًا للمستدعين الذين يستخدمون الاسم، وهذا يستحق التذكّر في مكتبة عامة.

المعاملات الاختيارية مقابل التحميل الزائد

قبل C# 4 كانت المرونة نفسها تحتاج إلى شكل محمّل لكل تركيبة. تدمجها المعاملات الاختيارية في دالة واحدة:

// overloads
static void Connect(string host) => Connect(host, 443);
static void Connect(string host, int port) => Connect(host, port, 30);
static void Connect(string host, int port, int timeoutSeconds) { /* ... */ }

// one method with optional parameters
static void Connect(string host, int port = 443, int timeoutSeconds = 30) { /* ... */ }

حين يوجد الاثنان يفضّل حل التحميل الزائد المرشّح الذي لا يحتاج إلى ملء أي قيمة افتراضية:

المخرجات:

Greet()
Greet(string) with Lena

تطابق Greet() الدالتين كلتيهما، فيختار المترجم التي لا معامل اختياري محذوف فيها. خلط التقنيتين على الاسم نفسه ينتج غالبًا استدعاءات يصعب التنبؤ بهدفها، فاختر واحدة لكل دالة.

اختر التحميل الزائد حين تحتاج الأشكال إلى شيفرة مختلفة أو أنواع معاملات مختلفة، والمعاملات الاختيارية حين لا تختلف إلا في القيم الافتراضية.

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

لا يُبحث عن القيمة الافتراضية وقت التشغيل. ينسخها المترجم في كل موضع استدعاء حين تُترجم الشيفرة المستدعية. تُترجم Connect("api.shop.com") إلى Connect("api.shop.com", 443, 30).

لذلك نتيجة على المكتبات. افترض أن الإصدار 1 من حزمة يأتي بـ Connect(string host, int timeoutSeconds = 30)، وأن الإصدار 2 يغيّر الافتراضي إلى 10. التطبيق المترجم مقابل الإصدار 1 يظل يمرّر 30 بعد أن تضع DLL الإصدار 2، حتى يُعاد ترجمة التطبيق نفسه. وإضافة معامل اختياري جديد إلى دالة عامة موجودة تكسر أيضًا المستدعين المترجمين سابقًا، لأن توقيع الدالة تغيّر وهم ما زالوا يبحثون عن القديم (MissingMethodException وقت التشغيل).

داخل تطبيق واحد يُترجم ككل لا يهمّ هذا أبدًا. أما لواجهات API العامة في حزم NuGet فإن التحميل الزائد (الذي يبقي القيم الافتراضية داخل المكتبة) أو القيمة الافتراضية null المحسومة في الجسم يتجنّبان المشكلة.

القيم الافتراضية والنوع المصرّح به

القاعدة نفسها وقت الترجمة تعني أنه حين تصرّح واجهة وصنف كلاهما بقيم افتراضية، تأتي القيمة الافتراضية من نوع المتغيّر الذي تستدعي عبره، لا من الكائن:

المخرجات:

printing "report" x5
printing "report" x1

الكائن نفسه، وقيمتان افتراضيتان مختلفتان. أبقِ القيم الافتراضية متطابقة بين الواجهة وتنفيذاتها، أو صرّح بها في مكان واحد فقط.

سمات معلومات المستدعي

تشغّل المعاملات الاختيارية أيضًا سمات معلومات المستدعي في System.Runtime.CompilerServices. يملؤها المترجم بتفاصيل موضع الاستدعاء:

المخرجات:

[Main:18] starting
[SaveOrder:13] order saved

بهذا تسجّل مكتبات السجلات مصدر الرسالة دون أن يكتبه المستدعي، وبهذا تحصل تنفيذات INotifyPropertyChanged على اسم الخاصية. وتضيف [CallerFilePath] مسار ملف المصدر بالطريقة نفسها.

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

كيف أجعل معاملًا اختياريًا في C#؟

امنحه قيمة افتراضية في التصريح: static void Log(string message, string level = "INFO"). عندئذ يستطيع المستدعون كتابة Log("started") أو Log("failed", "ERROR"). يجب أن تأتي المعاملات الاختيارية بعد كل المعاملات الإلزامية، ويجب أن تكون القيمة الافتراضية ثابتًا وقت الترجمة.

ما هي الوسائط المسمّاة في C#؟

وسائط تُمرَّر مع اسم المعامل: SendEmail(to: "ana@mail.com", urgent: true). تتيح لك تخطي المعاملات الاختيارية في المنتصف، وتمرير الوسائط بأي ترتيب، وجعل الاستدعاءات التي فيها true/false حرفية أو أرقام مقروءة من النظرة الأولى.

لماذا لا أستطيع استخدام DateTime.Now كقيمة افتراضية لمعامل؟

يجب أن تكون القيم الافتراضية ثوابت وقت الترجمة، وDateTime.Now تُحسب وقت التشغيل، فيبلّغ المترجم بـ CS1736، Default parameter value for 'at' must be a compile-time constant. استخدم معاملًا قابلًا لـ null بدلًا من ذلك: DateTime? at = null، ثم DateTime time = at ?? DateTime.Now; في الجسم.

هل أستخدم المعاملات الاختيارية أم التحميل الزائد للدوال في C#؟

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

ماذا يعني "Optional parameters must appear after all required parameters"؟

الخطأ CS1737: يلي معاملًا ذا قيمة افتراضية معامل بلا قيمة افتراضية. انقل المعاملات الإلزامية إلى البداية: (string to, bool urgent = false) لا (bool urgent = false, string to). بعد معامل اختياري لا يمكن أن يأتي إلا معاملات اختيارية أخرى أو مصفوفة params.

Coddy programming languages illustration

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

ابدأ الآن