אופרטור מקבל אופרנד אחד, שניים או שלושה ומפיק ערך: a + b, !done, x ?? fallback. האופרטורים של C# קרובים לאלה של C, Java ו-JavaScript, אבל כמה מהם מתנהגים בדרכים שמפתיעות מתחילים, ובראשם חילוק שלמים והסימן של %.
אופרטורים אריתמטיים
+, -, *, / ו-% עובדים על כל טיפוס מספרי. הטיפוס של התוצאה נקבע לפי האופרנדים: שני ערכי int מפיקים int, ו-int עם double מפיקים double.
פלט:
22
12
85
3
2
3.4
3.4
3
3.4
חילוק שלמים מקטם לכיוון אפס. 17 / 5 הוא 3 ו--17 / 5 הוא -3. זה גורם לבאג נפוץ מאוד: int percent = done / total * 100; הוא 0 בכל פעם ש-done < total, כי done / total כבר 0. הכפילו קודם (done * 100 / total) או חלקו כ-double.
חילוק של מספר שלם או decimal באפס זורק DivideByZeroException. חילוק של double באפס לא זורק; הוא מפיק אינסוף, או NaN עבור 0.0 / 0.0:
פלט:
True
True
False
DivideByZeroException
אופרטור המודולו ומספרים שליליים
% מחזיר את השארית של החילוק, והתוצאה שלו מקבלת את הסימן של האופרנד השמאלי. זה שונה מ-Python ומה-"mod" המתמטי, שתמיד אינם שליליים כשהמחלק חיובי:
פלט:
1
-1
1
2
even
62 min 5 s
Thu
1.5
השתמשו בפונקציית העזר Mod בכל פעם שהצד השמאלי יכול להיות שלילי והתוצאה היא אינדקס: days[(today + offset) % 7] היה זורק IndexOutOfRangeException עבור סכום שלילי. בבדיקת מספרים אי-זוגיים יש אותה מלכודת: n % 2 == 1 הוא false עבור מספרים אי-זוגיים שליליים; בדקו n % 2 != 0.
השמה, השמה מורכבת והגדלה
= מבצע השמה. הצורות המורכבות +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= משלבות פעולה עם השמה. ++ ו--- מוסיפים או מחסירים 1, ויש להם צורת תחילית וצורת סופית שנבדלות בערך שהן מחזירות:
פלט:
14
start, step
a=5 b=7 i=7
4
המקרה של byte מראה מוזרות: level = level + 10; לא מתקמפל, כי byte + int הוא int, אבל level += 10; כן מתקמפל, כי השמה מורכבת כוללת המרה מרומזת חזרה לטיפוס של המשתנה. ההמרה הזו מתגלגלת בגלישה (250 + 10 הופך ל-4).
אופרטורי השוואה ואופרטורים לוגיים
אופרטורי השוואה (==, !=, <, >, <=, >=) מחזירים bool. במחרוזות, == משווה את הטקסט, לא את ההפניות. האופרטורים הלוגיים הם ! (לא), && (וגם), || (או) ו-^ (או בלעדי).
פלט:
True
no active user
with ||:
evaluated A
with |:
evaluated A
evaluated B
True
&& ו-|| מקצרים: האופרנד הימני רץ רק כשהוא יכול לשנות את התוצאה. זה מה שהופך את user != null && user.IsActive לבטוח. & ו-| בודדים על ערכים בוליאניים תמיד מחשבים את שני הצדדים, וזה רק לעיתים רחוקות מה שרוצים בתנאי.
אופרטורי סיביות והזזה
על טיפוסים שלמים, &, |, ^ ו-~ פועלים על סיביות בודדות, ו-<< ו->> מזיזים אותן. משתמשים בהם לדגלים, למסכות, לגיבוב ולפרוטוקולים ברמה נמוכה:
פלט:
3
True
False
101
1024
125
5
-1
לקבוצות דגלים עם שמות, enum עם התכונה [Flags] ברור יותר מקבועים שלמים, ו-HasFlag קריא יותר ממסכה. C# 11 הוסיפה את >>>, הזזה ימינה ללא סימן שממלאת באפסים גם במספרים שליליים.
אופרטורי null
יש שלושה אופרטורים לעבודה עם ערכים שעשויים להיות null:
פלט:
Guest
0
5
a ?? b מחזיר את a אלא אם הוא null, ואז את b. a?.Member מחזיר null במקום לזרוק חריגה כש-a הוא null. C# 8 הוסיפה את a ??= b, שמציב את b ב-a רק כש-a הוא null. הם מוסברים לעומק בדף על טיפוסי nullable.
קדימות אופרטורים
כשביטוי משלב כמה אופרטורים, שורות גבוהות יותר נקשרות חזק יותר. אופרטורים באותה שורה מקובצים משמאל לימין, חוץ מהשמה, ?? ו-?:, שמקובצים מימין לשמאל. (האופרנדים עצמם תמיד מחושבים משמאל לימין.)
| קטגוריה | אופרטורים |
|---|---|
| ראשיים | x.y, x?.y, f(x), a[i], x++, x--, new, typeof, nameof |
| אונריים | +x, -x, !x, ~x, ++x, --x, (T)x, await |
| כפליים | *, /, % |
| חיבוריים | +, - |
| הזזה | <<, >>, >>> |
| יחס ובדיקת טיפוס | <, >, <=, >=, is, as |
| שוויון | ==, != |
| AND ברמת סיביות | & |
| XOR ברמת סיביות | ^ |
| OR ברמת סיביות | | |
| AND מותנה | && |
| OR מותנה | || |
| Null coalescing | ?? |
| מותנה | c ? a : b |
| השמה ולמבדה | =, +=, -=, ??=, =>, ושאר הצורות המורכבות |
שתי השלכות שכדאי להכיר: a + b * c מכפיל קודם, ו-x & mask == 0 משווה קודם (== נקשר חזק יותר מ-&), כך שמשמעותו x & (mask == 0) והוא לא מתקמפל עם מספרים שלמים. כתבו (x & mask) == 0. סוגריים לא עולים כלום והופכים את הכוונה לברורה.
העמסת אופרטורים
הטיפוסים שלכם יכולים להגדיר מה משמעות האופרטורים. כך decimal, DateTime ו-TimeSpan תומכים ב-+ וב-<:
פלט:
33.49
True
אופרטורים חייבים להיות public static, וחלקם מגיעים בזוגות מחייבים: הגדרה של < מחייבת את >, ו-== מחייב את !=. כש-== מוגדר, הקומפיילר גם מזהיר עד שתדרסו את Equals ואת GetHashCode בהתאם. העמיסו אופרטורים רק כשהמשמעות ברורה, כמו במספרים, וקטורים וכסף.
שאלות נפוצות
מה עושה % ב-C#?
% הוא אופרטור השארית: 17 % 5 הוא 2. לתוצאה יש את הסימן של האופרנד השמאלי, כך ש--7 % 3 הוא -1, ולא 2 כמו ב-Python. כדי לקבל תוצאה שתמיד אינה שלילית, כמו שצריך כשמגלגלים אינדקס, כתבו ((a % n) + n) % n. הוא עובד גם על double ועל decimal: 7.5 % 2 הוא 1.5.
למה 7 / 2 שווה 3 ב-C#?
כששני האופרנדים הם מספרים שלמים, / מבצע חילוק שלמים ומשליך את החלק השברי, בקיטום לכיוון אפס. הפכו אופרנד אחד לערך נקודה צפה או decimal כדי לקבל תוצאה שברית: 7 / 2.0 הוא 3.5, (double)a / b ממיר לפני החילוק, ו-7m / 2 הוא 3.5 בתור decimal.
מה ההבדל בין && ל-& ב-C#?
&& הוא AND מותנה: אם הצד השמאלי הוא false, הצד הימני לא מחושב. & על ערכים בוליאניים תמיד מחשב את שני הצדדים, ועל מספרים שלמים הוא AND ברמת הסיביות. השתמשו ב-&& וב-|| לתנאים, במיוחד בשומרים כמו user != null && user.IsActive, שעם & היו זורקים NullReferenceException.
מה ההבדל בין i++ ל-++i ב-C#?
שניהם מוסיפים 1 ל-i. הביטוי i++ מחזיר את הערך הישן, ו-++i את הערך החדש. בשורה נפרדת (i++;) אין הבדל. זה משנה רק כשמשתמשים בתוצאה, כמו ב-int a = i++;, ולכן הרבה מדריכי סגנון שומרים הגדלות בשורה משלהן.
מהו סדר הקדימות של האופרטורים ב-C#?
מהגבוה לנמוך: ראשיים (x.y, f(x), a[i], x++), אונריים (!, -, ++x, המרות), כפליים (* / %), חיבוריים (+ -), הזזות, יחס (< > <= >= is as), שוויון (== !=), &, ^, |, &&, ||, ??, האופרטור המותנה ?:, ולבסוף השמה. כשיש ספק, הוסיפו סוגריים.