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

المعامل الثلاثي (?:) في C#: الصياغة والأنواع والتداخل و??

يختار المعامل الشرطي condition ? a : b في C# إحدى قيمتين. كيف يُقيَّم، ولماذا يحتاج الفرعان إلى نوع مشترك (CS0173) والتحويل الذي يصلح ذلك، والمعاملات الثلاثية المتداخلة، والأقواس في استيفاء النصوص، ومتى تعبّر ?? و?. عن ذلك أفضل.

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

يختار المعامل الشرطي، الذي يُسمّى عادة المعامل الثلاثي (ternary)، بين قيمتين في تعبير واحد: condition ? valueIfTrue : valueIfFalse.

المخرجات:

0 items, shipping 4.99
1 item, shipping 4.99
5 items, shipping 0

يجب أن يكون الشرط bool. بعد تقييمه يُنفَّذ فرع واحد فقط من الفرعين؛ ولا يُقيَّم الآخر أبدًا. وهذا يجعل المعامل آمنًا للوصول المحروس مثل list.Count > 0 ? list[0] : "empty"، حيث كان تقييم list[0] على قائمة فارغة سيرمي استثناءً.

المعامل الثلاثي مقابل if/else

المعامل الثلاثي تعبير: ينتج قيمة تُسندها أو تمرّرها أو تعيدها. أما جملة if فتنفّذ عبارات. هاتان الدالتان تفعلان الشيء نفسه:

// with if/else
static string Status(int stock)
{
    if (stock > 0)
        return "in stock";
    else
        return "sold out";
}

// with the ternary
static string Status(int stock) => stock > 0 ? "in stock" : "sold out";

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

لا يستطيع المعامل الثلاثي أن يحلّ محل if لا تؤدي إلا أفعالًا. هذا لا يُترجم:

stock > 0 ? Ship(order) : Refund(order);
// error CS0201: Only assignment, call, increment, decrement, await, and new object expressions can be used as a statement

ولا يوجد معامل ثلاثي دون else: الجزءان ? و: إلزاميان دائمًا، لأن التعبير يجب أن يكون له قيمة في الحالتين.

يحتاج الفرعان إلى نوع مشترك

يستنتج المترجم نوع التعبير كله من فرعيه. يجب أن يتحوّل أحد الفرعين ضمنيًا إلى نوع الآخر. وحين لا يتحوّل أي منهما تحصل على CS0173:

var discount = isMember ? 10 : null;
// error CS0173: Type of conditional expression cannot be determined because
// there is no implicit conversion between 'int' and '<null>'

var label = inStock ? 1 : "none";
// error CS0173: ... between 'int' and 'string'

الإصلاح أن تحوّل أحد الفرعين إلى النوع الذي تريده، فيتحوّل الآخر إليه:

المخرجات:

no discount
7
3.5

لا تحتاج points / 2.0 : 0 إلى تحويل: 0 من نوع int، الذي يتحوّل ضمنيًا إلى double، فيكون التعبير double.

أضافت C# 9 استهداف النوع: حين يُسند التعبير إلى نوع مصرّح به يستخدم المترجم ذلك النوع. تُترجم int? discount = isMember ? 10 : null; في C# 9 وما بعده. أما مع var فلا نوع هدف، فتظل var discount = isMember ? 10 : null; تفشل في كل إصدار.

الأولوية والأقواس

أولوية المعامل الشرطي منخفضة جدًا، أقل من + و== و&&. هذا مريح في a > b ? a : b، ومفاجئ داخل ضمّ النصوص:

Console.WriteLine("Status: " + isActive ? "on" : "off");
// error CS0029: Cannot implicitly convert type 'string' to 'bool'

يقرأ المترجم الشرط على أنه "Status: " + isActive، أي نص. الأقواس حول المعامل الثلاثي تصلح ذلك: "Status: " + (isActive ? "on" : "off").

لـ استيفاء النصوص فخّه الخاص. داخل { } تبدأ النقطتان محدّد تنسيق ({price:F2})، فيربك المعامل الثلاثي المكشوف المحلّل. ضعه بين أقواس:

المخرجات:

2 users online
You have 1 unread message
Status: online

المعاملات الثلاثية المتداخلة

يُجمَّع المعامل من النهاية، فيُقرأ a ? x : b ? y : z على أنه a ? x : (b ? y : z). سلسلة صغيرة بشرط واحد في كل سطر سهلة القراءة:

المخرجات:

512 B
20 KB
5 MB

بعد مستويين أو ثلاثة، أو حين تختبر الشروط أشياء مختلفة، انتقل إلى if/else if أو تعبير switch. والتداخل داخل الفرع الصحيح (a ? (b ? x : y) : z) هو الشكل الذي يصعب تتبّعه أولًا.

?? و?. تحلّان محل معاملات ثلاثية شائعة

كثير من المعاملات الثلاثية موجودة فقط لمعالجة null. في C# معاملات أقصر لها:

المعامل الثلاثيالشكل الأقصرالمعنى
name != null ? name : "guest"name ?? "guest"قيمة بديلة عند null
customer != null ? customer.Email : nullcustomer?.Emailوصول إلى عضو يتسامح مع null
c != null && c.Address != null ? c.Address.City : "unknown"c?.Address?.City ?? "unknown"الاثنان معًا
value != null ? value : throw new ArgumentNullException(...)value ?? throw new ArgumentNullException(...)الفشل السريع

المخرجات:

Lena from Berlin
guest from unknown
guest from unknown

يُسمح أيضًا بتعبير throw في أي من فرعي المعامل الثلاثي: int age = input >= 0 ? input : throw new ArgumentOutOfRangeException(nameof(input));.

الشرطي المرجعي ref

منذ C# 7.2 يمكن للمعامل الثلاثي أن يختار بين متغيّرين بدل قيمتين، ويمكنك الإسناد عبر النتيجة:

// C# 7.2 and later
int wins = 0, losses = 0;
bool won = true;

ref int counter = ref (won ? ref wins : ref losses);
counter++;               // increments wins

يجب أن يكون الفرعان ref إلى متغيّرين من النوع نفسه. نادر في شيفرة التطبيقات؛ ويوجد أساسًا للشيفرة الحساسة للأداء التي تعمل مع بنى أو مصفوفات كبيرة دون نسخها.

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

ما هو المعامل الثلاثي في C#؟

هو المعامل الشرطي condition ? valueIfTrue : valueIfFalse، المعامل الوحيد في C# ذو المعاملات الثلاثة. يقيّم الشرط، ثم يقيّم ويعيد قيمة واحدة بالضبط من القيمتين: string label = age >= 18 ? "adult" : "minor";.

هل يمكنني استخدام المعامل الثلاثي دون else في C#؟

لا. الجزءان ? و: إلزاميان، لأن التعبير يجب أن ينتج قيمة دائمًا. إن أردت فقط فعل شيء حين يتحقق شرط فاستخدم جملة if. ولا يمكن للمعامل الثلاثي أن يقف وحده كعبارة: x > 0 ? A() : B(); هو الخطأ CS0201.

لماذا أحصل على "Type of conditional expression cannot be determined"؟

ليس للفرعين نوع مشترك، مثل var x = found ? 42 : null; (int وnull) أو found ? 1 : "none". حوّل أحد الفرعين كي يوجد نوع: found ? (int?)42 : null. ومنذ C# 9 يعمل أيضًا نوع هدف مصرّح به: int? x = found ? 42 : null; تُترجم، لكن var ما زالت تفشل.

كيف أستخدم المعامل الثلاثي داخل استيفاء النصوص؟

ضعه بين أقواس: $"{(count == 1 ? "item" : "items")}". دونها تُقرأ : كبداية محدّد تنسيق ولا تُترجم الشيفرة.

هل يمكن تداخل المعاملات الثلاثية في C#؟

نعم. يُجمَّع المعامل من النهاية، فتعني a ? x : b ? y : z التعبير a ? x : (b ? y : z). مستوى تداخل واحد موزّع على أسطر منفصلة يُقرأ جيدًا؛ أما السلاسل الأعمق فأوضح كسلسلة if/else if أو تعبير switch.

Coddy programming languages illustration

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

ابدأ الآن