لا تحوّل C# بين أنواع غير مترابطة من تلقاء نفسها أبدًا. النص الذي يحتوي على أرقام يبقى نصًا، وdouble لا يصير int بصمت. أنت تحوّل صراحة، والأداة التي تستخدمها تعتمد على ما تحوّله: نص إلى رقم (القراءة أو parsing)، أو نوع رقمي إلى آخر (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 هي الخيار الافتراضي للمدخلات والملفات وسلاسل الاستعلام. الدوال نفسها موجودة في كل نوع رقمي: 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)، وهذا المترجم يطلب منك الاختيار بين التحويل الصريح ودالة تقريب.
الأرقام والأنواع الأخرى إلى string
لكل نوع الدالة 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" ترميان استثناءً).
التحويلات المرجعية: التحويل الصريح وas وis
في أنواع الأصناف يفحص التحويل النوع الفعلي للكائن وقت التشغيل. التحويل صعودًا إلى صنف أساس ضمني. أما التحويل نزولًا إلى صنف مشتق فيحتاج إلى تحويل صريح، يرمي InvalidCastException حين يكون الكائن شيئًا آخر:
المخرجات:
4242
True
Card ending 4242, 50.00
InvalidCastException
5
cannot unbox int as long
فضّل is مع متغيّر حين تحتاج إلى التفرّع بحسب النوع؛ فهو فحص واحد، ولا يمكن أن يرمي استثناءً، والمتغيّر الجديد لا يكون في النطاق إلا حيث نجح الاختبار. واستخدم التحويل الصريح العادي حين يكون النوع المختلف خطأً يجب أن يفشل بوضوح. حالة int المغلّف توقع من يقرأ الأرقام من مجموعات 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 والتحويل الصريح في C#؟
يرمي التحويل (Dog)animal استثناء InvalidCastException إن لم يكن الكائن Dog. أما animal as Dog فتعيد null بدلًا من ذلك، لذا يجب أن يليها فحص null، ولا تعمل إلا مع الأنواع المرجعية والأنواع القابلة لـ null. البديل الحديث هو نمط is: if (animal is Dog dog) { ... } تختبر وتحوّل في خطوة واحدة.