C# אף פעם לא ממירה בעצמה בין טיפוסים שאינם קשורים. מחרוזת שמכילה ספרות היא עדיין מחרוזת, ו-double לא הופך בשקט ל-int. ממירים במפורש, והכלי תלוי במה שממירים: טקסט למספר (פענוח), טיפוס מספרי אחד לאחר (casting), או אובייקט לטיפוס ספציפי יותר (as, is).
String ל-int: Parse, TryParse ו-Convert
יש שלוש דרכים סטנדרטיות להפוך טקסט ל-int:
פלט:
2500
TryParse ok: 1250
TryParse failed, d = 0
42
-17
int.TryParse מחזירה bool וכותבת את התוצאה למשתנה out, שמקבל 0 במקרה של כישלון. מכיוון שקלט שגוי הוא דבר רגיל בכל מה שמשתמש מקליד, TryParse היא בחירת ברירת המחדל לקלט, לקבצים ול-query strings. אותן מתודות קיימות בכל טיפוס מספרי: long.TryParse, double.TryParse, decimal.Parse וכן הלאה.
מה כל אחת עושה עם קלט שגוי
שלוש המתודות נבדלות רק באופן שבו הן נכשלות:
פלט:
int.Parse(null) throws ArgumentNullException
Convert.ToInt32(null) = 0
int.Parse("abc") throws FormatException
int.Parse("12.5") throws FormatException
int.Parse("1,000") throws FormatException
int.Parse("9999999999") throws OverflowException
int.Parse("") throws FormatException
FormatException נושאת את ההודעה המוכרת "Input string was not in a correct format" (ב-.NET 8 ומעלה, "The input string '12.5' was not in a correct format", עם ציטוט הטקסט), והיא אחת משגיאות זמן הריצה הנפוצות ביותר ב-C#: "12.5" הוא לא int, וגם "1,000" לא. כדי לקבל אותם, פענחו כ-decimal, או אמרו למפענח אילו סגנונות מותרים:
פלט:
1000
255
13
10
תרבות: למה "1.5" יכול להפוך ל-15
פענוח משתמש בתרבות הנוכחית כברירת מחדל, ותרבויות לא מסכימות לגבי מפרידים. בגרמנית ובכמה תרבויות אירופיות אחרות המפריד העשרוני הוא פסיק והנקודה מקבצת אלפים. אותה מחרוזת נותנת מספרים שונים:
פלט:
1.5
15
1.5
קוד שקורא קובצי נתונים, JSON, קונפיגורציה או כל דבר שנוצר על ידי מכונה צריך תמיד להעביר CultureInfo.InvariantCulture, גם בפענוח וגם בעיצוב. אחרת אותה תוכנית עובדת במחשב של המפתח וקוראת לא נכון כל מספר בשרת שמוגדר לשפה אחרת. השתמשו בתרבות הנוכחית רק לטקסט שאדם הקליד או יקרא.
המרות מספריות מרומזות ומפורשות
המרות מרחיבות קורות באופן מרומז: int ל-long, int ל-double, float ל-double, int ל-decimal. המרה שעלולה לאבד את סדר הגודל של הערך או את החלק השברי שלו דורשת המרה מפורשת (type)value. חריג אחד שכדאי לזכור: int או long ל-float, ו-long ל-double, הן מרומזות למרות שהן עלולות לאבד דיוק: float f = 16_777_217; מתקמפל ושומר 16777216.
פלט:
9 -9
10
2
4
705032704
9.99
7 7.0 7
שלושה דברים לזכור. המרה מנקודה צפה למספר שלם חותכת; היא לא מעגלת. Convert.ToInt32(double) מעגלת, בעיגול בנקאי (חצאים הולכים לשכן הזוגי). והמרה של מספר שלם שלא נכנס מתגלגלת בשקט, אלא אם היא רצה בהקשר checked, שבו היא זורקת OverflowException (ראו טיפוסי נתונים).
int x = 3.7; היא שגיאת קומפילציה (CS0266: cannot implicitly convert type 'double' to 'int', an explicit conversion exists), וזה הקומפיילר שמבקש מכם לבחור בין המרה לבין מתודת עיגול.
מספרים וטיפוסים אחרים למחרוזת
לכל טיפוס יש ToString(), וטיפוסים מספריים מקבלים מחרוזת פורמט. אינטרפולציית מחרוזות קוראת לאותו עיצוב:
פלט:
42
000042
1234.50
1,234.50
$1,234.50
FF
1010
True
Order 000042: 1234.50
קודי הפורמט (D, F, N, C, X, P, E) ותבניות מותאמות כמו "0.00" מכוסים ב-אינטרפולציית מחרוזות.
תווים וערכים בוליאניים
פלט:
7
55
7
7
A
True
False
1
False
(int)'7' הוא 55, הקוד של התו, לא 7. זה מכשיל כל מי שמחבר ספרות של מספר; החסירו '0' או השתמשו ב-char.GetNumericValue. bool.Parse מקבלת "true" ו-"false" בכל שילוב של אותיות גדולות וקטנות, ושום דבר אחר ("yes" ו-"1" זורקים).
המרות הפניה: casts, as ו-is
עבור טיפוסי מחלקה, המרה בודקת את הטיפוס האמיתי של האובייקט בזמן ריצה. המרה כלפי מעלה למחלקת בסיס היא מרומזת. המרה כלפי מטה למחלקה נגזרת צריכה המרה מפורשת, שזורקת InvalidCastException כשהאובייקט הוא משהו אחר:
פלט:
4242
True
Card ending 4242, 50.00
InvalidCastException
5
cannot unbox int as long
העדיפו is עם משתנה כשצריך להסתעף לפי הטיפוס; זו בדיקה אחת, היא לא יכולה לזרוק, והמשתנה החדש קיים רק במקום שבו הבדיקה הצליחה. השתמשו בהמרה רגילה כשטיפוס אחר יהיה באג שצריך להיכשל ברעש. המקרה של int ארוז (boxed) מכשיל אנשים שקוראים מספרים מאוספי object או מתאי DataRow: פרקו קודם לטיפוס המדויק ((long)(int)boxed), או השתמשו ב-Convert.ToInt64(boxed), שמטפלת בכל טיפוס מספרי.
שאלות נפוצות
איך ממירים מחרוזת ל-int ב-C#?
השתמשו ב-int.TryParse(text, out int number) כשהטקסט מגיע ממשתמש, מקובץ או מהרשת: היא מחזירה false במקום לזרוק כשהטקסט אינו מספר תקין. השתמשו ב-int.Parse(text) כשטקסט לא תקין הוא באג וחריגה היא התגובה הנכונה. Convert.ToInt32(text) מתנהגת כמו int.Parse, חוץ מזה שהיא מחזירה 0 עבור null.
מה ההבדל בין int.Parse ל-Convert.ToInt32?
עבור מחרוזות הן כמעט זהות: שתיהן זורקות FormatException על טקסט כמו "abc" ו-OverflowException על מספרים מחוץ לטווח. ההבדל הוא null: int.Parse(null) זורקת ArgumentNullException, בעוד ש-Convert.ToInt32(null) מחזירה 0. Convert.ToInt32 מקבלת גם טיפוסים אחרים (double, bool, object), ומעגלת double למספר השלם הזוגי הקרוב במקום לחתוך.
מה פירוש "Input string was not in a correct format"?
זו ההודעה של FormatException שנזרקת על ידי int.Parse, double.Parse או Convert.ToInt32 (ב-.NET 8 ומעלה היא נקראת "The input string 'abc' was not in a correct format", עם ציטוט הטקסט) כשהטקסט אינו מספר בפורמט הצפוי: אותיות, מחרוזת ריקה, נקודה עשרונית כשמפענחים int, או מפריד אלפים. עברו ל-TryParse כדי לטפל בקלט שגוי בלי חריגה, ובדקו באיזו תרבות המספר נכתב.
איך ממירים double ל-int ב-C#?
כתבו המרה מפורשת: int n = (int)price;. ההמרה חותכת לכיוון אפס, כך ש-(int)9.99 הוא 9 ו-(int)-9.99 הוא -9. כדי לעגל במקום זאת, השתמשו ב-(int)Math.Round(price), ושימו לב ש-Math.Round מעגלת חצאים למספר הזוגי הקרוב כברירת מחדל; העבירו MidpointRounding.AwayFromZero לעיגול כמו בבית הספר.
מה ההבדל בין as להמרה (cast) ב-C#?
המרה (Dog)animal זורקת InvalidCastException אם האובייקט אינו Dog. animal as Dog מחזירה null במקום זאת, ולכן חייבת לבוא אחריה בדיקת null, והיא עובדת רק עם טיפוסי הפניה וטיפוסים שמאפשרים null. החלופה המודרנית היא תבנית is: if (animal is Dog dog) { ... } בודקת וממירה בצעד אחד.