C#: تحويل الأنواع
1. التحويل الضمني
يتم إجراء التحويل الضمني تلقائيًا بواسطة المُجمِّع دون الحاجة إلى أي صيغة إضافية. ويحدث هذا التحويل عند التعيين من نوع أضيق إلى نوع أوسع، ولا يؤدي أبدًا إلى فقدان البيانات.
(1) قواعد التحويل
int→longint→doublefloat→double- أي نوع ←
string(عبرToString())
▶ مثال
int a = 100;
long b = a;
int c = 42;
double d = c;
float e = 3.14f;
double f = e;
int g = 99;
string h = g.ToString();
Console.WriteLine($"int→long: {b}");
Console.WriteLine($"int→double: {d}");
Console.WriteLine($"float→double: {f}");
Console.WriteLine($"int→string: {h}");
int→long: 100
int→double: 42
float→double: 3.14
int→string: 99
2. التحويل الصريح
يتطلب التحويل الصريح استخدام صيغة التحويل، حيث يُوضع النوع المستهدف بين قوسين قبل القيمة. ويحدث هذا عند التعيين من نوع أوسع إلى نوع أضيق، وقد يؤدي إلى فقدان البيانات.
(1) قواعد النحو
TargetType variable = (TargetType)value;
▶ مثال
double pi = 3.14;
int truncated = (int)pi;
int number = 42;
double widened = (double)number;
long big = 3000000000L;
int overflow = (int)big;
Console.WriteLine($"(int)3.14 → {truncated}");
Console.WriteLine($"(double)42 → {widened}");
Console.WriteLine($"(int)3000000000L → {overflow}");
(int)3.14 → 3
(double)42 → 42
(int)3000000000L → -1294967296
تحذير: قد يؤدي التحويل الصريح إلى فقدان الدقة أو حدوث تجاوز. تأكد دائمًا من نطاق البيانات قبل إجراء التحويل.
3. تحويل الفئة
توفر فئة System.Convert طرقًا ثابتة للتحويل بين أنواع أساسية متنوعة. وهي أكثر أمانًا وأكثر تعبيرًا من التحويل المباشر.
(1) الطرق الشائعة
| الطريقة | الوصف |
|---|---|
Convert.ToInt32 |
يُحوَّل إلى int |
Convert.ToDouble |
يُحوَّل إلى double |
Convert.ToString |
يُحوَّل إلى string |
Convert.ToBoolean |
يُحوَّل إلى bool |
▶ مثال
string s1 = "42";
string s2 = "3.14";
string s3 = "0";
int n1 = Convert.ToInt32(s1);
double n2 = Convert.ToDouble(s2);
string n3 = Convert.ToString(n1);
bool n4 = Convert.ToBoolean(s3);
bool n5 = Convert.ToBoolean(1);
Console.WriteLine($"ToInt32: {n1}");
Console.WriteLine($"ToDouble: {n2}");
Console.WriteLine($"ToString: {n3}");
Console.WriteLine($"ToBoolean(\"0\"): {n4}");
Console.WriteLine($"ToBoolean(1): {n5}");
ToInt32: 42
ToDouble: 3.14
ToString: 42
ToBoolean("0"): False
ToBoolean(1): True
نصيحة: لا يتعرف
Convert.ToBooleanإلا على"True"و"False"بالنسبة للسلاسل النصية. أما بالنسبة للقيم الرقمية، فإن القيم غير الصفرية تُرمز لها بـtrue، والصفر يُرمز له بـfalse.
4. Parse و TryParse
(1) طريقة التحليل
تقوم Parse بتحويل سلسلة إلى النوع المستهدف، وتُطلق استثناءً من نوع FormatException إذا كان التنسيق غير صالح.
مثال
int a = int.Parse("42");
double b = double.Parse("3.14");
Console.WriteLine($"int.Parse: {a}");
Console.WriteLine($"double.Parse: {b}");
int.Parse: 42
double.Parse: 3.14
try
{
int bad = int.Parse("hello");
}
catch (FormatException ex)
{
Console.WriteLine($"Parse failed: {ex.Message}");
}
Parse failed: Input string was not in a correct format.
(2) طريقة TryParse
لا تُطلق TryParse أي استثناء أبدًا. فهي تُرجع bool تشير إلى نجاح العملية، وتُخرج القيمة التي تم تحليلها عبر المعلمة out.
مثال
if (int.TryParse("42", out int result1))
{
Console.WriteLine($"Parse succeeded: {result1}");
}
if (!int.TryParse("abc", out int result2))
{
Console.WriteLine($"Parse failed, result2 = {result2}");
}
Parse succeeded: 42
Parse failed, result2 = 0
نصيحة: يُفضل استخدام
TryParseعند التعامل مع مدخلات غير موثوقة، مثل بيانات المستخدم، لتجنب التأثير السلبي للاستثناءات على الأداء.
5. التغليف والتفريغ
(1) الملاكمة
التعبئة (Boxing) هي عملية تحويل نوع القيمة إلى نوع مرجعي object. يتم نسخ القيمة من المكدس إلى الذاكرة المؤقتة، مما يتطلب تخصيص مساحة في الذاكرة.
مثال
int value = 42;
object boxed = value;
Console.WriteLine($"Boxed: {boxed}, Type: {boxed.GetType()}");
Boxed: 42, Type: System.Int32
(2) فتح العبوة
يُعرف «Unboxing» بأنه عملية التحويل الصريح لمُشير object إلى نوع قيمة. وتتطلب هذه العملية تحويلاً صريحاً، ويؤدي عدم تطابق الأنواع إلى إثارة استثناء InvalidCastException.
مثال
object boxed = 42;
int unboxed = (int)boxed;
Console.WriteLine($"Unboxed: {unboxed}");
Unboxed: 42
object boxed = 42;
try
{
double wrong = (double)boxed;
}
catch (InvalidCastException ex)
{
Console.WriteLine($"Unbox type mismatch: {ex.Message}");
}
Unbox type mismatch: Specified cast is not valid.
تحذير: يجب أن يتطابق نوع الهدف في عملية فك التعبئة تمامًا مع النوع الأصلي المُعبَّأ، وإلا فسيتم إصدار استثناء
InvalidCastException.
6. الأخطاء الشائعة في تحويل الأنواع
(1) فقدان الدقة عند التحويل من العدد العائم إلى العدد الصحيح
double d = 9.99;
int i = (int)d;
Console.WriteLine($"9.99 → {i}");
9.99 → 9
(2) التدفق من النوع العريض إلى النوع الضيق
long big = 5000000000L;
int small = (int)big;
Console.WriteLine($"5000000000L → {small}");
5000000000L → 705032704
(3) إلقاء استثناءات عند وجود سلاسل غير صالحة
try
{
int n = int.Parse("3.14");
}
catch (FormatException)
{
Console.WriteLine("Cannot parse \"3.14\" as int");
}
Cannot parse "3.14" as int
نصيحة - أفضل الممارسات لتجنب الأخطاء: تحقق من نطاقات البيانات قبل التحويل؛ وافضل استخدام
TryParse؛ واستخدم الكلمة الرئيسيةcheckedفي حالات تجاوز السعة.
❓ أسئلة شائعة
📖 ملخص
- يتم التحويل الضمني من الأنواع الأضيق إلى الأنواع الأوسع، وهو آمن وتلقائي
- يستخدم التحويل الصريح صيغة التحويل، وقد يؤدي إلى فقدان الدقة أو حدوث تجاوز السعة
- توفر فئة Convert طرقًا آمنة للتحويل بين الأنواع المختلفة
- تقوم دالة Parse بتحليل السلاسل مباشرةً وتُطلق استثناءً في حالة الفشل؛ بينما تُرجع دالة TryParse قيمة منطقية بأمان
- عملية التغليف (boxing) تُغلف نوع القيمة ككائن مُخصص في الـ«هياب»؛ أما عملية فك التغليف (unboxing) فتتطلب تطابقًا تامًا في النوع
- يُفضل استخدام TryParse في كود الإنتاج لتجنب الاستثناءات والتحويل الضمني إلى نوع صفيفي
📝 تمارين
- اكتب برنامجًا يقوم بتحويل قيمة
shortالتي تبلغ 32767 إلىintبشكل ضمني، ويقوم بتحويل قيمةintالتي تبلغ 32768 إلىshortبشكل صريح، ثم راقب النتائج - استخدم
Convert.ToInt32لتحويل 2.5 و3.5، وتحقق من طريقة التقريب التي يتبعها البنك - اكتب دالة تقبل معلمة من نوع سلسلة، وتستخدم
TryParseلتحليلها بأمان إلىint، وتُرجع القيمة التي تم تحليلها في حالة النجاح، أو -1 في حالة الفشل - اكتب كودًا يوضح عمليتي التعبئة والتفريغ، ثم قم بالتفريغ عمدًا باستخدام نوع غير صحيح واكتشف الخطأ
InvalidCastException - قم بتغليف عملية تحويل
intالتي تتسبب في حدوث تجاوز بالسعة باستخدام الكلمة الرئيسيةchecked، ثم لاحظ ما إذا كان سيتم إلقاء استثناءOverflowExceptionأم لا