C#: تحويل الأنواع

1. التحويل الضمني

يتم إجراء التحويل الضمني تلقائيًا بواسطة المُجمِّع دون الحاجة إلى أي صيغة إضافية. ويحدث هذا التحويل عند التعيين من نوع أضيق إلى نوع أوسع، ولا يؤدي أبدًا إلى فقدان البيانات.

(1) قواعد التحويل

▶ مثال

CSHARP
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}");
▶ جرّب الكود
TEXT 📖 للعرض فقط
int→long: 100
int→double: 42
float→double: 3.14
int→string: 99

2. التحويل الصريح

يتطلب التحويل الصريح استخدام صيغة التحويل، حيث يُوضع النوع المستهدف بين قوسين قبل القيمة. ويحدث هذا عند التعيين من نوع أوسع إلى نوع أضيق، وقد يؤدي إلى فقدان البيانات.

(1) قواعد النحو

CSHARP
TargetType variable = (TargetType)value;

▶ مثال

CSHARP
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}");
▶ جرّب الكود
TEXT 📖 للعرض فقط
(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

▶ مثال

CSHARP
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}");
▶ جرّب الكود
TEXT 📖 للعرض فقط
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 إذا كان التنسيق غير صالح.

مثال

CSHARP
int a = int.Parse("42");
double b = double.Parse("3.14");
Console.WriteLine($"int.Parse: {a}");
Console.WriteLine($"double.Parse: {b}");
▶ جرّب الكود
TEXT 📖 للعرض فقط
int.Parse: 42
double.Parse: 3.14
CSHARP
try
{
    int bad = int.Parse("hello");
}
catch (FormatException ex)
{
    Console.WriteLine($"Parse failed: {ex.Message}");
}
TEXT 📖 للعرض فقط
Parse failed: Input string was not in a correct format.

(2) طريقة TryParse

لا تُطلق TryParse أي استثناء أبدًا. فهي تُرجع bool تشير إلى نجاح العملية، وتُخرج القيمة التي تم تحليلها عبر المعلمة out.

مثال

CSHARP
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}");
}
▶ جرّب الكود
TEXT 📖 للعرض فقط
Parse succeeded: 42
Parse failed, result2 = 0

نصيحة: يُفضل استخدام TryParse عند التعامل مع مدخلات غير موثوقة، مثل بيانات المستخدم، لتجنب التأثير السلبي للاستثناءات على الأداء.


5. التغليف والتفريغ

(1) الملاكمة

التعبئة (Boxing) هي عملية تحويل نوع القيمة إلى نوع مرجعي object. يتم نسخ القيمة من المكدس إلى الذاكرة المؤقتة، مما يتطلب تخصيص مساحة في الذاكرة.

مثال

CSHARP
int value = 42;
object boxed = value;
Console.WriteLine($"Boxed: {boxed}, Type: {boxed.GetType()}");
▶ جرّب الكود
TEXT 📖 للعرض فقط
Boxed: 42, Type: System.Int32

(2) فتح العبوة

يُعرف «Unboxing» بأنه عملية التحويل الصريح لمُشير object إلى نوع قيمة. وتتطلب هذه العملية تحويلاً صريحاً، ويؤدي عدم تطابق الأنواع إلى إثارة استثناء InvalidCastException.

مثال

CSHARP
object boxed = 42;
int unboxed = (int)boxed;
Console.WriteLine($"Unboxed: {unboxed}");
▶ جرّب الكود
TEXT 📖 للعرض فقط
Unboxed: 42
CSHARP
object boxed = 42;
try
{
    double wrong = (double)boxed;
}
catch (InvalidCastException ex)
{
    Console.WriteLine($"Unbox type mismatch: {ex.Message}");
}
TEXT 📖 للعرض فقط
Unbox type mismatch: Specified cast is not valid.

تحذير: يجب أن يتطابق نوع الهدف في عملية فك التعبئة تمامًا مع النوع الأصلي المُعبَّأ، وإلا فسيتم إصدار استثناء InvalidCastException.


6. الأخطاء الشائعة في تحويل الأنواع

(1) فقدان الدقة عند التحويل من العدد العائم إلى العدد الصحيح

CSHARP
double d = 9.99;
int i = (int)d;
Console.WriteLine($"9.99 → {i}");
TEXT 📖 للعرض فقط
9.99 → 9

(2) التدفق من النوع العريض إلى النوع الضيق

CSHARP
long big = 5000000000L;
int small = (int)big;
Console.WriteLine($"5000000000L → {small}");
TEXT 📖 للعرض فقط
5000000000L → 705032704

(3) إلقاء استثناءات عند وجود سلاسل غير صالحة

CSHARP
try
{
    int n = int.Parse("3.14");
}
catch (FormatException)
{
    Console.WriteLine("Cannot parse \"3.14\" as int");
}
TEXT 📖 للعرض فقط
Cannot parse "3.14" as int

نصيحة - أفضل الممارسات لتجنب الأخطاء: تحقق من نطاقات البيانات قبل التحويل؛ وافضل استخدام TryParse؛ واستخدم الكلمة الرئيسية checked في حالات تجاوز السعة.


❓ أسئلة شائعة

س هل التحويل الضمني لا يؤدي أبدًا إلى فقدان البيانات؟
ج صحيح. لا يحدث التحويل الضمني إلا عبر مسارات آمنة لا يؤدي فيها إلى فقدان الدقة أو النطاق.
س ما الفرق بين Convert.ToInt32 والتحويل إلى (int)؟
ج يقوم (int) بالاقتطاع مباشرةً، بينما تقوم Convert.ToInt32 بتقريب القيم العائمة باستخدام تقريب المصرفيين.
س كيف يؤثر التعبئة (boxing) وإلغاء التعبئة (unboxing) على الأداء؟
ج تعمل عملية التعبئة على تخصيص الذاكرة في الـ«هياب»، بينما تتطلب عملية إلغاء التعبئة فحص النوع ونسخ القيمة. وتؤدي تكرار هذه العمليات إلى انخفاض الأداء.
س أيهما أفضل: int.Parse أم Convert.ToInt32؟
ج عند إدخال سلسلة نصية، تعمل الدالتان بشكل مشابه، لكن Convert.ToInt32 تُرجع القيمة 0 عند وجود قيمة null، بينما تُطلق int.Parse استثناءً.
س ما هي قيمة المعلمة out عندما تفشل دالة TryParse؟
ج يتم تعيينها إلى القيمة الافتراضية للنوع، على سبيل المثال، 0 بالنسبة إلى int.

📖 ملخص

📝 تمارين

  1. اكتب برنامجًا يقوم بتحويل قيمة short التي تبلغ 32767 إلى int بشكل ضمني، ويقوم بتحويل قيمة int التي تبلغ 32768 إلى short بشكل صريح، ثم راقب النتائج
  2. استخدم Convert.ToInt32 لتحويل 2.5 و3.5، وتحقق من طريقة التقريب التي يتبعها البنك
  3. اكتب دالة تقبل معلمة من نوع سلسلة، وتستخدم TryParse لتحليلها بأمان إلى int، وتُرجع القيمة التي تم تحليلها في حالة النجاح، أو -1 في حالة الفشل
  4. اكتب كودًا يوضح عمليتي التعبئة والتفريغ، ثم قم بالتفريغ عمدًا باستخدام نوع غير صحيح واكتشف الخطأ InvalidCastException
  5. قم بتغليف عملية تحويل int التي تتسبب في حدوث تجاوز بالسعة باستخدام الكلمة الرئيسية checked، ثم لاحظ ما إذا كان سيتم إلقاء استثناء OverflowException أم لا
Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%