مقدمه

تبدیل نوع (type conversion) یکی دیگر از مفاهیم کلیدی در برنامه‌نویسی است که هر زبانی آن را به روش خودش پیاده‌سازی کرده است. تبدیل یک نوع به نوعی دیگر می‌تواند به صورت ضمنی یا صریح انجام شود. تبدیل صریح توسط برنامه‌نویس انجام می‌شود و تبدیل ضمنی بدون دخالت برنامه‌نویس توسط کامپایلر به صورت خودکار انجام می‌شود. حالا موضوعی که باید به آن پرداخته شود این است که اولاً قوانین حاکم بر تبدیلات ضمنی در C# چیست و کامپایلر در چه مواقعی یک تبدیل ضمنی را انجام می‌دهد و ثانیاً وقتی نیاز به انجام یک تبدیل صریح داشته باشیم، این کار به چه روش‌هایی قابل انجام است.

تبدیل ضمنی در C#

C# در کار با داده‌ها یک زبان سخت‌گیر است. برخی از نشانه‌های این سخت‌گیری را در درس قبل دیدیم. برای نمونه، دیدیم که در این زبان، مقادیر لیترال دارای یک نوع پیش‌فرض هستند؛ اعداد صحیح از نوع int و اعداد اعشاری از نوع double در نظر گرفته می‌شوند. به علاوه، گفتیم که در دو طرف = که عملگر تخصیص (assignment operator) نامیده می‌شود، باید مقادیر هم‌نوع داشته باشیم. به همین دلیل است که گزاره‌ای مثل float x = 1.2; با خطا همراه می‌شود؛ چون در سمت چپ یک متغیر float داریم و در سمت راست یک مقدار double. این مشکل را با اضافه کردن پسوند f به عدد سمت راست حل کردیم. این کار در واقع، یک تبدیل صریح است.

اما حالا به گزاره‌ی double x = 8; فکر کنید. اینجا هم ما نوع‌های ناسازگار داریم؛ در سمت چپ، نوع double و در سمت راست، نوع int. اما این گزاره با خطایی همراه نیست. اینجا پای یک تبدیل ضمنی در میان است؛ یعنی خود کامپایلر به صورت خودکار، کار تبدیل عدد صحیح 8 به double را انجام داده است.

یک تبدیل صریح (explicit conversion) تبدیلی است که توسط برنامه‌نویس انجام می‌شود و تبدیل ضمنی (implicit conversion) تبدیلی است که به طور خودکار توسط کامپایلر انجام می‌شود. حالا سؤال مهم این است که کامپایلر در چه مواقعی یک تبدیل ضمنی را انجام می‌دهد؟

قاعده‌ی کلی این است که: وقتی در برنامه نوع‌های ناسازگار داشته باشیم، در صورتی که امکان از دست رفتن اطلاعات وجود نداشته باشد، کامپایلر یک تبدیل ضمنی را انجام می‌دهد و مشکل ناسازگاری نوع‌ها را بدون سر و صدا حل می‌کند. اما اگر امکان از دست رفتن اطلاعات وجود داشته باشد، تبدیلی انجام نمی‌دهد و یک خطا در مورد ناسازگاری نوع‌ها گزارش می‌کند.

پس، در گزاره‌ی double d = 8; کامپایلر به این دلیل یک تبدیل ضمنی را انجام می‌دهد که در تبدیل یک int به double امکان از دست رفتن اطلاعات وجود ندارد؛ چون هر int یک double هم هست یا به عبارت بهتر، چون اعداد صحیح زیرمجموعه‌ی اعداد اعشاری هستند. اما گزاره‌ی float x = 1.2; نیازمند یک تبدیل از double به float است که می‌تواند با از دست رفتن اطلاعات همراه باشد؛ چون float زیرمجموعه‌ی double نیست. یعنی ممکن است در سمت راست، یک مقدار double داشته باشیم که به نوع float متعلق نباشد.

حالا به مثال زیر دقت کنید.

Copy Icon Program.cs
int i = 8;
double d;
d = i;

در اینجا در گزاره‌ی آخر، نوع‌های ناسازگار داریم اما چون در تبدیل int به double با خطر از دست رفتن اطلاعات مواجه نیسیتیم، یک تبدیل ضمنی رخ داده است. اما حالا کد زیر را ببینید.

Copy Icon Program.cs
double d = 8;
int i;
i = d;

در گزاره‌ی اول، یک تبدیل ضمنی رخ داده اما گزاره‌ی سوم با خطا همراه است. علت اینکه تبدیل ضمنی برای گزاره‌ی سوم انجام نمی‌شود، این است که در تبدیل double به int امکان از دست رفتن اطلاعات وجود دارد. دقت کنید که ذخیره‌ی یک مقدار صحیح در متغیر d نمی‌تواند ضامن انجام تبدیل باشد؛ چون همانطور که قبلاً هم گفتیم، کامپایلر کاری به مقدار متغیرها ندارد، بلکه نوع آنها را در نظر می‌گیرد. کامپایلر نمی‌تواند مثل ما فکر کند و استدلال کند که در اینجا مقدار صحیح 8 در d ذخیره شده، بلکه اینطور به قضیه نگاه می‌کند که متغیر d از چه نوعی است و پتانسیل دریافت چه مقادیری را دارد.

تبدیل صریح در C#

دیدیم که وقتی امکان از دست رفتن اطلاعات وجود داشته باشد، کامپایلر تبدیلی را به صورت ضمنی انجام نمی‌دهد. در چنین مواقعی، اگر بخواهیم تبدیلی انجام شود، باید آن را به طور صریح اعلام کنیم.

Copy Icon Program.cs
double d = 2.25;
int i;
i = (int)d; // i = 2

در اینجا با قرار دادن نوع مقصد int درون پرانتز و قبل از متغیرِ از نوع مبدأ double، کامپایلر را وادار کرده‌ایم تا یک تبدیل از double به int انجام دهد. اگرچه این تبدیل با امکان از دست رفتن اطلاعات همراه است، اما چون ما صراحتاً آن را درخواست کرده‌ایم، کامپایلر فرض می‌کند که ما این تبدیل را به صورت آگاهانه انجام می‌دهیم. در نهایت، مقدار صحیح 2 در متغیر i قرار می‌گیرد.

متدهای کلاس Convert

روش‌های تبدیل صریح که تا اینجا دیدیم، برای تبدیل نوع‌های عددی به یکدیگر کاربرد دارند. اما برای انجام تبدیل‌های پیچیده‌تر، مثل تبدیل رشته به عدد، باید از متدهای یک کلاس با نام Convert استفاده کنیم. برخی از مهمترین این متدها عبارتند از:

  • متد Convert.ToInt32() آرگومانش را (در صورت امکان) به int تبدیل می‌کند. اگر آرگومان این متد یک عدد اعشاری باشد، با حذف قسمت اعشار، به int تبدیل می‌شود. اگر آرگومان از نوع string باشد، چنانچه درون رشته یک عدد صحیح باشد، به همان عدد تبدیل می‌شود و در غیر این صورت، یک خطای زمان اجرا تولید می‌شود. در مورد آرگومان‌های بولین هم مقدار true به 1 و مقدار false به صفر تبدیل می‌شود. متدهای مشابهی برای تبدیل به سایر نوع‌های صحیح مثل ToInt64() برای تبدیل به long وجود دارد.
  • متد Convert.ToDouble() آرگومانش را (در صورت امکان) به double تبدیل می‌کند. اگر آرگومان این متد از نوع string باشد، چنانچه درون رشته یک عدد اعشاری یا صحیح باشد، همان عدد را به صورت اعشاری برمی‌گرداند و در غیر این صورت، خطای زمان اجرا تولید می‌شود. مقدار بولبن true به 1.0 و مقدار false به 0.0 تبدیل می‌شود.
  • متد Convert.ToBoolean() آرگومانش را (در صورت امکان) به یک bool تبدیل می‌کند. در مورد آرگومان‌های عددی، مقادیر 1 و 1.0 به true و سایر مقادیر به false تبدیل می‌شوند. در مورد آرگومان‌های رشته‌ای، رشته‌ی "true" به مقدار بولین true و رشته‌ی "false" به مقدار بولین false تبدیل می‌شود و هر رشته‌ی دیگری منجر به بروز خطای زمان اجرا می‌شود.

در مثال زیر از متدهای بالا استفاده شده است.

Copy Icon Program.cs
int i = Convert.ToInt32(1.25);  // i = 1 
int j = Convert.ToInt32("25");  // j = 25 
int k = Convert.ToInt32(true);  // k = 1 
int l = Convert.ToInt32(false);  // l = 0  
            
double x = Convert.ToDouble(2);  // x = 2.0 
double y = Convert.ToDouble(3.25m);  // y = 3.25 
double z = Convert.ToDouble("1.25");  //z = 1.25 
double w = Convert.ToDouble(true);  // w = 1.0 
            
bool p = Convert.ToBoolean(0);  // p = false 
bool q = Convert.ToBoolean(5);  // q = true 
bool r = Convert.ToBoolean("false");  // r = false 
bool s = Convert.ToBoolean("true");  // s = true