آموزش تبدیل نوع داده در سی شارپ

Ratings
(0)

 قبلاً در این کتاب، دیدیم که تمام داده ها، صرفنظر از نوع شان، دنباله ای از بیت ها هستند؛ یعنی دنباله ای از صفرها و یک ها می باشند. معنای متغیر، با نحوه تفسیر این داده ها تعیین می شود. ساده ترین نوع متغیر، نوعِ char می باشد. این نوع، با استفاده از یک عدد، یک کاراکتر را در مجموعه کاراکترهای یونیکد(Unicode) مشخص می کند.

در حقیقت عدد مذکور، دقیقاً مانند یک متغیر از نوع ushort ذخیره می شود. هردوی این نوع ها، یک عدد از 0 تا 65535 را  در خود ذخیره می کنند. اما، به طور کلی، نوع های مختلف متغیرها، از روش های مختلف برای نمایش داده ها استفاده می کنند.

این نشان می دهد که حتی اگر این امکان وجود داشت که دنباله ی بیت ها را از یک متغیر به یک متغیر از نوع مختلف قرار دهیم، نتایج ممکن است آنچه ما انتظار داریم نباشند. (شاید آنها از مقدار ذخیره سازی مشابه استفاده کنند، یا شاید نوع متغیر مورد نظر، فضای ذخیره سازی کافی برای گنجاندن تمام بیت های منبع را داشته باشد).

به جای این نگاشت یک به یک از بیت ها، از یک متغیر به یک متغیر دیگر، نیاز داریم از تبدیل نوع داده ها(Type conversion) استفاده کنیم. تبدیل نوع، به دو صورت است:

1. تبدیل نوع ضمنی(Implicit): در این روش، تبدیل نوع A به نوع B در تمام شرایط امکان پذیر است؛ و قواعد انجام تبدیل به اندازه کافی برای ما راحت هستند که بتوانیم به رایانه اطمینان کنیم.

2. تبدیل صریح(Explicit): در این روش، تبدیل از نوع A به نوع B تنها در برخی شرایط امکان پذیر است؛ یا اینکه در جایی امکان پذیر است که قواعد برای تبدیل، پیچیده هستند و به نوعی نیاز به پردازش بیشتر دارند.


تبدیل نوع ضمنی(Implicit) در سی شارپ

تبدیل ضمنی نیاز به انجام دادن کاری یا نیاز به کدنویسی اضافی ندارد. به کد زیر توجه کنید:

var1 = var2;

اگر نوع متغیر var2 بتواند به صورت ضمنی به نوع متغیر var1 تبدیل شود، این انتساب، این انتساب ممکن است یک تبدیل ضمنی باشد. اما این انتساب، به سادگی می تواند شامل دو متغیر باشد که نوع یکسانی داشته باشند؛ در این مورد، نیازی به تبدیل ضمنی نیست.

بعنوان مثال، مقادیر ushort و char به خوبی (به یکدیگر) تبدیل پذیر هستند، زیرا هردو یک عدد بین 0 و 65535 را ذخیره می کنند. ما می توانیم مقادیر بین این نوع ها را به صورت ضمنی به یکدیگر تبدیل کنیم؛ این موضوع در کدهای زیر نشان داده شده است:

ushort destinationVar;
char sourceVar = 'a';
destinationVar = sourceVar;
WriteLine($"sourceVar val: {sourceVar}");
WriteLine($"destinationVar val: {destinationVar}");

 در کدهای بالا، مقدار ذخیره شده در sourceVar در destinationVar ذخیره می شود. وقتی که در خروجی، با استفاده از دو فرمان WriteLine() این متغیرها را چاپ کنیم، خروجی زیر را مشاهده می کنیم:

sourceVar val: a
destinationVar val: 97

با وجود اینکه این دو متغیر، اطلاعات یکسانی را در خود ذخیره می کنند، اما با استفاده از نوعی که دارند، به روش های مختلفی تفسیر می شوند. در نوع های ساده، تبدیل های ضمنی زیادی وجود دارد. نوع های bool و string تبدیل ضمنی ندارند اما نوع های عددی، تعداد تبدیل ضمنی دارند.

بعنوان یک مرجع، می توانید به جدول 5.1 رجوع کنید. این جدول، تبدیل های عددی که کامپایلر می تواند به صورت ضمنی انجام دهد را نشان می دهد(به یاد داشته باشید که نوع های char مانند اعداد ذخیره می شوند، بنابراین char ،یک نوع عددی یا numeric به حساب می آید).

(جدول 5.1 : تبدیل های ضمنی عددی)

 نگران نباشید، نیازی نیست این جدول را حفظ کنید؛ زیرا تشخیص اینکه کامپایلر می تواند به طور ضمنی کدام تبدیل را انجام دهد بسیار آسان است. قبلاً در فصل 3، در جدول 3.1 و 3.2 و 3.3 محدوده ی مقادیر امکان پذیر برای هر نوع ساده ی عددی، نشان داده شده است.

قاعده ی تبدیل ضمنی برای این نوع ها، این است: هر نوعِ A که محدوده ی(range) مقادیر امکان پذیر آن، کاملاً در محدوده ی مقادیر امکان پذیر نوعِ B قرار دارد، می تواند به طور ضمنی، به آن نوع تبدیل شود. دلیل این کار ساده است. اگر سعی کنیم یک مقدار را در یک متغیر قرار دهیم، اما آن مقدار بیرون از محدوده مقادیری باشد که این متغیر می تواند دریافت کند، آنگاه با مشکل مواجه خواهیم شد.

بعنوان مثال، یک متغیر از نوع short قادر است اعدادی را تا 32767 در خود ذخیره کند و ماکزیمم مقداری که مجاز هستیم در یک متغیر از نوع byte قرار دهیم، 255 است؛ بنابراین اگر سعی کنیم یک مقدار از نوع short را در یک مقدار از نوع byte قرار دهیم، با مشکل مواجه می شویم.

اگر متغیر از نوع short یک مقدار بین 256 و 32767 را در خود داشته باشد، در یک متغیر از نوع byte جا نمی شود. اگر بدانید که مقداری که در متغیر از نوع short قرار گرفته است، کمتر از 255 است، آیا می توانیم این مقدار را به byte تبدیل کنیم؟ پاسخ این است که می توانیم اما باید از یک تبدیل صریح(explicit) استفاده کنیم.

انجام یک تبدیل صریح(explicit) مانند این است که بگوییم: بسیار خوب، می دانم که شما در مورد انجام این کار به من هشدار داده اید، اما من مسئولیت اتفاقات را بر عهده می گیرم.


تبدیل صریح(Explicit) در سی شارپ

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

byte destinationVar;
short sourceVar = 7;
destinationVar = sourceVar;
WriteLine($"sourceVar val: {sourceVar}");
WriteLine($"destinationVar val: {destinationVar}");

اگر سعی کنید کد بالا را کامپایل کنید، خطای زیر را دریافت خواهید کرد:

Cannot implicitly convert type 'short' to 'byte'. An explicit conversion exists
(are you missing a cast?)

برای اینکه این کد کامپایل شود، نیاز داریم کدی اضافه کنیم که یک بتدیل صریح را انجام دهد. ساده ترین راه برای انجام این کار در این متن، این است که متغیر short را به byte تبدیل(cast) کنیم. (همانطور که در خطای بالا توصیه شده است).  تبدیل نوع(Casting)، یعنی داده ای را از یک نوع، به نوع دیگری تبدیل کنیم؛ برای انجام کار، می توانیم از شیوه ی نوشتاری(سینتکس) زیر استفاده کنیم:

(<destinationType>)<sourceVar>

این باعث می شود مقدار <sourceVar> به <destinationType> تبدیل شود.


نکته: در برخی موقعیت ها، Casting (تبدیل نوع داده) تنها راهِ امکان پذیر می باشد. انواعی که ارتباط کمی با یکدیگر دارند یا اصلاً ارتباطی ندارند، احتمالاً  تبدیل(casting) برای آنها تعریف نشده است.


بنابراین، می توانیم مثال خود را با استفاده از سینتکس زیر، اصلاح کنیم تا یک نوع short را به byte تبدیل کنیم:

byte destinationVar;
short sourceVar = 7;
destinationVar = (byte)sourceVar;
WriteLine($"sourceVar val: {sourceVar}");
WriteLine($"destinationVar val: {destinationVar}");

کد بالا خروجی زیر را تولید می کند:

sourceVar val: 7
destinationVar val: 7

اگر سعی کنیم یک مقدار را مجبور کنیم تا به یک نوع متغیر نامناسب تبدیل شود، چه خواهد شد؟ بعنوان مثال، ما نمی توانیم یک عدد صحیح(integer) بزرگ را به یک نوع عددی که بسیار کوچک باشد، تبدیل کنیم. کد زیر، این موضوع را نشان می دهد:

byte destinationVar;
short sourceVar = 281;
destinationVar = (byte)sourceVar;
WriteLine($"sourceVar val: {sourceVar}");
WriteLine($"destinationVar val: {destinationVar}");

نتیجه به صورت زیر خواهد بود:

sourceVar val: 281
destinationVar val: 25

چه اتفاقی افتاد؟ برای پاسخ، نگاهی به اعداد باینری این دو عدد می اندازیم، و ماکزیمم مقداری که می تواند در یک بایت ذخیره شود، که 255 است، را بررسی می کنیم:

281 = 100011001
25 = 000011001
255 = 011111111

می توانید ببینید که بیت سمت چپ از داده ی منبع(source data) یعنی 281 از بین رفته است. این باعث می شود این سوال بلافاصله به ذهن بیاید: چطور می‌توانیم تشخیص دهیم که این اتفاق چه زمانی می‌افتد؟ واضح است که مواقعی هستند که نیاز داریم به طور صریح یک نوع داده را به نوع دیگری تبدیل(cast) کنیم، و خوب است بدانیم که آیا در این مسیر داده‌ای از دست رفته است یا خیر. عدم تشخیص این موضوع می‌تواند باعث ایجاد خطاهای مهمی شود. بعنوان مثال، می توانیم یک اپلیکیشن حسابداری یا یک اپلیکیشن برای تعیین مسیر موشک به سمت ماه را مثال بزنیم.

یک راه برای انجام این کار، این است که مقدار متغیر منبع(source variable) را بررسی کنیم و آن را با حدود(limits) شناخته شده ی متغیر مقصد(destination) مقایسه کنیم. یک روش دیگر این است که در زمان اجرا، سیستم را مجبور کنیم تا توجه خاصی به تبدیل انجام دهد. 

اگر تلاش کنیم تا یک مقدار را در یک متغیر قرار دهیم و این مقدار برای نوع آن متغیر بسیار بزرگ باشد، باعث می شود سرریز(overflow) شود؛ و این وضعیتی است که می خواهیم آن را بررسی کنیم. دو کلمه کلیدی برای بررسی سرریز برای یک عبارت وجود دارد: checked و unchecked. برای استفاده از آنها به صورت زیر عمل می کنیم:

checked(<expression>)
unchecked(<expression>)

ما می توانیم در مثال قبلی، اجبار به بررسی سرریز(overflow) کنیم:

byte destinationVar;
short sourceVar = 281;
destinationVar = checked((byte)sourceVar);
WriteLine($"sourceVar val: {sourceVar}");
WriteLine($"destinationVar val: {destinationVar}");

وقتی که این کد اجرا شود، با پیام خطای نشان داده شده در تصویر 5.1 از کار می‌افتد(این برنامه در یک پروژه به نام OverflowCheck قرار دارد).

(تصویر 5.1)

خطای برنامه آموزش سی شارپ

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

برای انجام این کار، properties را برای پروژه ی خود اصلاح کنید. برای انجام این کار، در پنجره ی Solution Explorer (روی آن) راست-کلیک کنید و گزینه ی Properties را انتخاب کنید. در سمت چپ پنجره، روی Build کلیک کنید تا تنظیمات Build بالا بیاید. خاصیتی که می خواهیم تغییر دهیم، یکی از تنظیمات Advanced است، بنابراین روی دکمه ی Advanced کلیک کنید.

 در جعبه ی مکالمه ای که ظاهر می شود، جعبه ی Arithmetic Overflow/Underflow را تیک بزنید؛ همان طور که در تصویر 5.2 نشان داده شده است. به طور پیش فرض، این تنظیم غیرفعال است؛ اگر آن را فعال کنیم،  وضعیت checked که قبلا توضیح داده شد، ارائه داده می شود. این ویژگی می تواند بر سعت اجرای برنامه تاثیر بگذارد. بنابراین وقتی که دیگر به آن نیاز ندارید، آن را غیرفعال کنید.

 

  • بازدید: 245

نوشتن دیدگاه

لطفا نظرات خود را بیان کنید. به سوالات در سریع ترین زمان پاسخ داده خواهد شد.اما به نکات زیر توجه کنید:
1. سعی کنید نظرات شما مرتبط با مقاله ی مورد نظر باشد، در غیر این صورت پاسخ داده نخواهد شد.
2. سوالات خود را به صورت کوتاه بیان کنید و از پرسیدن چند سوال به طور همزمان خودداری کنید.
3. سوال خود را به طور واضح بیان کنید و از کلمات مبهم استفاده نکنید.

ارسال