آموزش گام به گام #C (جلسه بیست و دوم: نکاتی در مورد نوع در #C)
1398/01/27 11:41 , میلاد صاحب نظر

آموزش گام به گام #C (جلسه بیست و دوم: نکاتی در مورد نوع در #C)

گاهی کار با نوع‌ها اعصاب خورد کن می‌شود. مثلا وقتی می‌خواهید مقدار متغیری از یک نوع را به متغیری از نوع متفاوت اختصاص دهید با ارورهایی مواجه می‌شوید که شاید متوجه دلیل آن‌ها نشوید. همه چیز دلیل موجهی دارد. با ما همراه باشید!

جلسه اول: آغاز کار با #C جلسه ششم: namespaceها جلسه یازدهم: اندیس گذار یا indexerها جلسه شانزدهم: استفاده از اتریبیوت‌ها جلسه بیست و یکم: متدهای بی‌نام یا anonymous
جلسه دوم: عملگرها، نوع‌ها و متغیرها جلسه هفتم: مقدمه‌ای بر کلاس‌های #C جلسه دوازدهم: ساختار یا structها جلسه هفدهم: enumها جلسه بیست و دوم: موضوعاتی در مورد نوع در #C
جلسه سوم: عبارات کنترلی - گزینشی جلسه هشتم: وراثت کلاس در #C جلسه سیزدهم: واسط یا interfaceها جلسه هجدهم: عملگرهای overloading جلسه بیست و سوم: کار با نوع‌های nullable
جلسه چهارم: عبارات کنترلی - حلقه‌ها جلسه نهم: چندریختی جلسه چهاردهم: مقدمه‌ای بر delegateها و رویدادها جلسه نوزدهم: کپسوله‌سازی  
جلسه پنجم: متدها جلسه دهم: ویژگی یا propertyها جلسه پانزدهم: مقدمه‌ای بر کنترل خطا یا exception جلسه بیستم: مقدمه‌ای بر generic collectionها  

در طول این دوره، با انواع بسیار متنوعی از نوع‌ها آشنا شدید، به خصوص آن‌هایی که نوع‌های مخصوص #C بودند و آن‌هایی که خودتان طراحی می‌کردید.

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

برای مثال، نمی‌توانید یک double را بدون استفاده از یک عملگر cast (به منظور انجام عملیات تبدیل) به یک int اختصاص دهید.

ویژگی دیگر نوع در #C به تفاوت‌های بین رفرنس و نوع‌های مقداری مربوط می‌شود. چنین مشکلاتی حتماً این سؤال را برای شما پیش آورده‌اند که چرا این‌چنین است و در این جلسه به همین موضوعات می‌پردازیم.

اهداف این جلسه عبارت‌اند از:

  • درک نیاز به type safety
  • درک چگونگی تبدیل یک نوع به نوع دیگر
  • آموختن نوع‌های رفرنس
  • آموختن نوع‌های مقداری
  • درک تفاوت‌های معنایی بین نوع‌های رفرنس و مقداری

چرا type safety؟

در زبان‌های نوع نشده (Untyped Language)، مانند زبان‌های اسکریپت نویسی، می‌توانید یک متغیر را به متغیر دیگر اختصاص دهید و کامپایلر یا مفسر برای فهمیدن و درک اینکه این فرآیند اختصاص باید چگونه انجام شود، از یک الگوریتم هوشمند استفاده‌ می‌کند.

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

برای مثال، اگر یک مقدار int را به یک متغیر float اختصاص دهید، فرآیند تبدیل بدون هیچ مشکلی انجام می‌شود چون قسمت اعشاری float جدید، صفر خواهد بود. به هر حال، اگر برعکس عمل کنید و یک مقدار float را به یک متغیر int اختصاص دهید، آن وقت با مشکل مواجه خواهید شد.

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

یافتن چنین ارورهایی مشکل و پر هزینه است، مخصوصاً اگر ارور تا زمانی که برنامه به مرحله تولید برسد خودش را نشان ندهد (مرحله تولید یعنی محصول به دست کاربر نهایی رسیده است).

استفاده از عملگر Cast برای تبدیلات

در جلسه دوم، نوع‌ها و عملگرهای #C را آموختید. در آن جلسه، سایز و دقت نوع‌های مختلف توضیح داده شدند و لیستی از عملگرهای موجود ارائه شد.

عملگر Cast، یعنی (x)، در جدول ۴-۲ در ابتدای جدول به عنوان یک عملگر اصلی معرفی شده است. وقتی باید نوعی را به نوعی دیگر تبدیل کنید که با هم تناسب ندارند، باید این کار را از طریق روشی که به آن تبدیل صریح گفته می‌شود انجام دهید. در این روش از عملگر Cast استفاده می‌شود.

لیست ۱-۲۲ مثالی از یک تبدیل ضمنی (که به عملگر Cast نیاز ندارد) و یک تبدیل صریح را نشان می‌دهد.

لیست ۱-۲۲. عملگرهای Cast

using System;

class Program
{
    static void Main()
    {
        float lengthFloat = 7.35f;

        // از بین رفتن دقت – تبدیل صریح
        int lengthInt = (int)lengthFloat;

        // بدون مشکل – تبدیل ضمنی
        double lengthDouble = lengthInt;

        Console.WriteLine("lengthInt = " + lengthInt);
        Console.WriteLine("lengthDouble = " + lengthDouble);
        Console.ReadKey();
    }
}

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

چون یک مقدار Float یعنی lengthFloat دارای بخش اعشاری است، اما یک مقدار int یعنی lengthInt بخش اعشاری ندارد، پس نوع‌ها با هم سازگار نیستند. به خاطر ویژگی type safety، زبان #C به شما اجازه اختصاص مستقیم lengthFload به lengthInt را نمی‌دهد.

به خاطر حفاظت از برنامه، باید از یک عملگر Cast (یعنی (int)) استفاده کنید تا به اجبار lengthFloat به lengthInt تبدیل صریح شود. در خروجی، مشاهده می‌کنید که مقدار lengthInt برابر با ۷ است، این نشان می‌دهد که بخش اعشاری مقدار 7.35f از lengthFloat از دست رفته است.

اختصاص از lengthInt به lengthDouble امن است و مشکلی ندارد، چون یک مقدار Double شصت و چهار بیت است و یک مقدار int سی و دو بیت، یعنی اینکه هیچ اطلاعاتی را از دست نخواهید داد.

از این رو، تبدیل از نوع ضمنی است، بدان معنا که می‌توانید عمل اختصاص را بدون نیاز به عملگر cast انجام دهید.

درک نوع‌های رفرنس

متغیرهای نوع رفرنس نام خیلی مناسبی دارند(رفرنس) چون متغیر یک آدرس به سمت شیء را در خود نگه می‌دارد. در زبان‌های C و ++C، یک ویژگی مشابه رفرنس به نام pointer یا اشاره‌گر دارید، که به سمت یک شیء اشاره می‌کند.

اگرچه می‌توانید یک اشاره‌گر را تغییر دهید، اما نمی‌توانید مقدار یک رفرنس را تغییر دهید – اشاره‌گر فقط به یک شیء در حافظه اشاره می‌کند.

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

لیست ۲-۲۲. اختصاص نوع رفرنس

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

در لیست ۲-۲۲ من دو instance یا نمونه از Employee به نام‌های joe و bob ایجاد کردم. در خروجی مشاهده می‌کنید که propertyهای Name از هر دو نمونه‌های Employee، هر کدام مقادیر اختصاص داده شده به آن‌ها (از زمانی که شیء تازه ایجاد شد) را نمایش می‌دهند.

بعد از اختصاص مقدار joe به bob، مقدار propertyهای Name از هر دو نمونه‌ها یکسان است. این چیزی است که انتظار داریم ببینیم.

چیزی که ممکن است شما را شگفت‌زده کند، مقادیری هستند که بعد از اختصاص دادن یک مقدار به متغیر نمونه Employee به نام joe به وجود می‌آیند. اگر با دقت به کد توجه کنید، متوجه می‌شوید که کد مقدار bob را تغییر نمی‌دهد – فقط مقدار joe تغییر می‌کند.

به هر حال، نتایج خروجی نشان می‌دهند که Name Property در bob مشابه Name Property در joe است. این نشان می‌دهد که بعد از اختصاص joe به bob، هر دو متغیرها رفرنس‌هایی به سمت شیء joe در خود نگه داشته‌اند. فقط رفرنس کپی شده شده است – نه خود شیء.

به همین دلیل نتایج چاپ شده Name در هر دو نمونه‌های joe و bob یکی است، چون تغییر روی شیئی انجام شده است که هر دو نمونه به آن اشاره می‌کنند.

نوع‌های زیر نوع‌های رفرنسی هستند:

  • آرایه‌ها
  • کلاس
  • Delegateها
  • واسط‌ها

نکته: نوع اولیه رشته یا String نیز یک نوع رفرنسی است.

درک نوع‌های مقداری

متغیرهای نوع مقداری، همان‌طور که از نام آن‌ها (یعنی مقداری) بر می‌آید، مقدار شیء را در خود نگهداری می‌کنند. یک متغیر نوع مقداری یک کپی از یک شیء نگهداری می‌کنند و وقتی عمل اختصاص از یک متغیر نوع مقداری به یک متغیر دیگر انجام می‌شود، دو طرف عملگر اختصاص دارای دو کپی مجزا از آن مقدار خواهند بود.

لیست ۳-۲۲ چگونگی انجام عمل اختصاص نوع مقداری را نشان می‌دهد.

لیست ۳-۲۲. اختصاص نوع مقداری

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

در لیست ۳-۲۲ مشاهده می‌کنید که Inches Property ازbob  و joe در ابتدا با مقادیر مختلفی مقداردهی شدند. بعد از اختصاص joe به bob، عمل کپی شدن مقدار اتفاق می‌افتد، که در نتیجه هر دو متغیر دارای یک مقدار می‌شوند، اما دو کپی متفاوت وجود دارد.

برای نمایش نتایج اختصاص مقدار، توجه کنید که بعد از قرار دادن مقدار joe برابر با ۶۵ چه اتفاقی می‌افتد. خروجی نشان می‌دهد که bob تغییر نکرده است، این نشان می‌دهد که نوع‌های مقداری کپی‌های مجزایی از شیء‌های خود نگهداری می‌کنند .

نوع‌های زیر از جمله نوع‌های مقداری هستند:

  • Enum
  • Struct

نکته: همه نوع‌های اولیه (int، Char، double و غیره) به جز رشته یا String، نوع‌های مقداری هستند.

تفاوت‌های نوع رفرنس و نوع مقداری

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

خلاصه

در این جلسه چند نکته در مورد کار با نوع‌ها در #C گفته شدند. حالا باید درک بهتری از type safety و اینکه چگونه به شما کمک می‌کند از مشکلات دوری کنید داشته باشید.

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

برای نمایش تفاوت‌های بین نوع‌های رفرنس و نوع‌های مقداری، در این جلسه مثال‌هایی ارائه شدند که نشان دادند نوع‌های رفرنس و مقداری در طول انجام عمل اختصاص چه رفتاری از خود نشان می‌دهند.

منبع: C#-Station

جلسه بعد                                                                جلسه قبل

 مطالب مرتبط

 مقدمه ای بر زبان برنامه نویسی #C و پلتفرم NET Framework.
7 دلیل منطقی برای آموختن #C
 طبقه بندی زبان های برنامه نویسی
به دست آوردن Exceptionها در #C و یافتن همه خطاها
 LINQ(زبان جستجوی یکپارچه)
برنامه نویسی شیءگرا چیست؟

از آخرین دوره های آموزشی و تخفیف ها مطلع شوید

با تکمیل فرم زیر ، از اخبار و اطلاعات به روز برنامه نویسی و تکنولوژی عقب نمانید

آخرین مطالب

آموزش جامع SQL Server (جلسه ۱۲)
آموزش جامع SQL Server (جلسه ۱۲)

دستور UPDATE در SQL Server برای تغییر داده‌های موجود در یک جدول، از دستور UPDATE به شکل زیر استفاده ...

آموزش جامع SQL Server (جلسه ۱۵)
آموزش جامع SQL Server (جلسه ۱۵)

دستور DROP TABLE در SQL Server گاهی، لازم است یک جدول که دیگر استفاده‌ای ندارد را حذف کنید. برای ...

آموزش جامع SQL Server (جلسه ۳۵: Window Functionها – بخش ۲)
آموزش جامع SQL Server (جلسه ۳۵: Window Functionها – بخش ۲)

بخش اول از آخرین مبحث دوره جامع آموزش SQL Server در جلسه قبلی بررسی شد. این مبحث که ...

آخرین دیدگاه ها

دیدگاه خود را درباره این پست بنویسید

فرم ارسال نظرات