امکانات جدید سی شارپ در نسخه های 6، 7، 8 و 9 برای پیرمردها - بخش دوم و آخر

برای دیدن ویدیوهای من در مورد برنامه نویسی عضو این کانال شوید :https://t.me/mediapub_channelامکانات جدید سی شارپ در نسخه های 6، 7، 8 و 9 برای پیرمردها - بخش اول امکانات جدید سی شارپ در نسخه های 6، 7، 8 و 9 برای پیرمردها - بخش دوم و آخر (همین مقاله)توضیحات این دو مقاله را در یک ویدیو ببینید : https://youtu.be/xBfLKe2D32Q سی شارپ 8این نسخه از سی شارپ تغییرات بزرگی در زمینه ویژگی های کار با null داشته است. کلا اصطلاح نال حدود 50 سال قدمت دارد و شبیه یک سیاه چاله است که یک آبجکت میتواند دچار آن شود.حتما چنین خطایی را بارها دیده اید. چون متغیر middle نال است پس پراپرتی های آن وجود خارجی ندارند و برنامه با خطا مواجه میشود.مشکلی که تا حالا در خطاهای مرتبط با نال وجود داشت این است که از روی خطا معلوم نیست این null از کجا آمده؟ چرا ظاهر شده؟ خطای برنامه نویسی است؟ خطای ران تایم است؟ برای حل این مشکلات فیچری در سی شارپ 8 اضافه شده که با nullable reference type ها کار میکند.بطور کلی reference type ها بر خلاف value type ها همیشه در سی شارپ قابلیت نال شدن داشتند. یعنی پیش فرض این بود ولی در value type ها با یک علامت سوال اینکار را میکردیم.یکی از کارهایی که در سی شارپ هشت به کمک ما می آید این است که اخطار نال بگیریم، یعنی اخطاری ببینیم مبنی بر اینکه ممکن است در محل x یک اکسپشن null reference ببینید. این ویژگی باید در سی شارپ 8 فعال شود، یعنی به طور خودکار فعال نیست. به فایل csproj مراجعه کنید و در PropertyGroup خط زیر را اضافه کنید. با اینکار refrence type ها دیگر نالیبل نخواهند بود.<Nullable>enable</Nullable>کلاس فرضی Person که سه پراپرتی دارد ولی در کانستراکتور تنها دو پراپرتی اولیه سازی شده اند و پراپرتی MiddleName در بین آنها نیست.با توجه به تصویر فوق میبینیم که بعد از فعال کردن nullable در فایل csproj، خط سبزی زیر کانستراکتور نشان داده میشود که وقتی ماوس را روی آن میبیریم این خطا را میبینیم :به پراپرتی «غیرنالیبلِ» MiddleName را مقدار اولیه نداده اید. اینکار را میکنیم.اخطار دیگری میبینیم که نمیتوانید null را به یک پراپرتی غیر نالیبل نسبت دهید! با تنظیم جدید در csproj، دیگر string ها نالیبل نیستند. پس اصلاح زیر را انجام میدهیم.حالا به برنامه ای که از این کلاس استفاده میکند برگردیم.به خط سبز رنگ اخطار زیر middle دقت کنید.در برنامه اصلی اخطاری میبینیم که «متغیر middle ممکن است نال باشد» و این همان چیزی هست که میخواستیم. حین توسعه توسط IDE این اخطار را میبینیم و با انجام اصلاحات در آینده با خطای null exception مواجه نمیشویم. کد را به شکل زیر اصلاح میکنیم.خط سبز رنگ اخطار، محو شد.و در نهایت متد را به شکل زیر اصلاح میکنیم تا خطوط کمتری را اشغال کند.اگر MiddleName نال باشد دیگر به پراپرتی Length اهمیت نمیدهد و نال برمیگرداند و چون علامت ?? داریم، چون نال شده صفر برمیگرداند.توسعه ویژگی Pattern matchingدر نسخه 7 دیدیم که Pattern matching تحول بزرگی در سی شارپ ایجاد کرد. در نسخه 8 ادامه این تحول با ایجاد پترن های زیر تکمیل شد.ویژگی Property matchingswitch (location) { case { State: &quotMN&quot }: ... }فکر نکنم نیاز به توضیح داشته باشد. اگر مقدار پراپرتی location.State برابر با "MN" باشد این case عمل میکند. به شکل زیر هم قابل بررسی است.if (location is { State: &quotMN&quot })یا به شکل زیر :if (sword is Sword { Damage: 10, Durability: var durability }) { // code executes if Damage = 10 // durability has value of sword.Durability }یعنی داخل if میرویم اگر sword از جنس Sword باشد و sword.Damage=10 باشد، همچنین عملیات durability=sword.Durability انجام شود که بتوانیم داخل اسکوپ از durability استفاده کنیم.تایپ و پراپرتی را با هم و داخل switch نیز میتوان چک کرد:switch (vehicle) { case Taxi { Occupants: 2 } t: ... }اگر تایپ vehicle از نوع Taxi بود و vehicle.Occupants=2 بود این case عمل میکند.ویژگی Tuple patterns https://gist.github.com/2d40f4feb2ee2d097e850a8c2dcca4d0 به کمک این روش میتوانیم چندین مقدار را در سوئیچ مقایسه کنیم.در سی شارپ نسخه 8 میتوانیم سوئیچ خود را به شکل بهینه تر بنویسیم. https://gist.github.com/2423339d67ae1e6491ad8167988eb99c تغییرات زیر اتفاق افتاده است :متغیری که در سمت چپ قرار است مقداری به آن assign شود، یکبار نوشته شده و مثل سوئیچ قدیمی چند بار تکرار نشده است.کلمه switch بعد از مقادیری که میخواهیم بررسی کنیم آورده شده، در سوئیچ قدیمی کلمه switch اول می آمد.از کلمه case استفاده نشده است.علامت : بین الگو و بدنه با علامت => جایگزین شده است.به جای break از کاما (,) استفاده شده است.در انتها از _ به جای default استفاده شده است.تحول در کار با ایندکس ها و رنجقبل از هر چیز مثال مایکروسافت را ببینید :کافیست مثال زیر را ببینید https://gist.github.com/00a659d01f6377f972aec2133b0d0862 زیبا نیست؟تغییر در کار با usingپیش از این از using در کد برای مشخص کردن محدوده عمر یک متغیر استفاده میکردیم. در حقیقت در انتهای اسکوپ متد Dispose اجرا میشد. در سی شارپ 8 راه حل جدید ارائه شده است. https://gist.github.com/da52402d282c1400a22e920cffc7019c در این روش متد Dispose زمانی که اسکوپ به انتها میرسد بطور خودکار اجرا میشود.ویژگی Static local functionاین ویژگی به شکل غیر استاتیک در نسخه 7 معرفی شده بود. در حالت استاتیک مقادیر بیرون فانکشن استاتیک قابل دسترس نیست(در حالت غیر استاتیک به مقادیر فانکنشنی که این فانکنشن در آن تعریف شده دسترسی داشتیم) ولی میتوان به متد استاتیک مقادیر را پاس داد.سی شارپ 9ویژگی Init-Only propertyبه تصویر زیر نگاه کنیدآبجکت توسط کانستراکتور مقدار دهی اولیه شده و دیگر نمیتوان مقدار پراپرتی را تغییر داد. (به دلیل وجود init)ما یک کلاس Person داریم (در تصویر فوق کلمه Person زیر متن خطا پنهان شده).کمی توضیح لازم است. فرض کنید از کلاس زیر به عنوان مدل استفاده میکنید https://gist.github.com/dd6da1d964f3acf1a80a22aa5a87a296 در این کلاس پراپرتی Name به شکل private است یعنی برای اولیه سازی یک آبجکت از جنس این کلاس مجبوریم یا برای این کلاس یک کانستراکتور بنویسیم که مقدار Name را پر کند یا یک متد public به نام SetName تعریف کنیم. اما معمولا برنامه نویس ها برای مدل سازی از ساختار پراپرتی های کلاس استفاده میکنند و مایکروسافت بالاخره این موضوع را فهمید!پس باز هم به کد بالا دقت کنید. قرار است همین ساختار (فقط پراپرتی) را داشته باشیم به علاوه اینکه امکان مقدار دهی اولیه به آبجکت داشته باشیم. یعنی «فقط» هنگام ساخت آبجکت بتوانیم مقدار دهیم و مقدار پراپرتی بعدا نتواند عوض شود.فرض کنید پراپرتی های فوق از نوع init باشند. میتوانیم هنگام ساخت آبجکت به آنها مقدار اولیه بدهیم. معمولا ما علاقمندیم به شکل فوق object initialize را انجام دهیم. با استفاده از private کردن set نمیتوانیم اینکار را بکنیم و مجبوریم از اولیه سازی به روش کانستراکتور یا یک متد public به عنوان setter استفاده کنیم.این ویژگی بر اساس درخواست immutable شدن یک آبجکت در سی شارپ ایجاد شده است. یعنی ما دوست نداریم یک آبجکت بعد از اولیه سازی تغییر کند.تایپ Recordتصویر قبلی را دوباره ببینید. کلاسی داریم که با کلیدواژه init ما را مجبور میکند فقط بتوانیم آبجکت immutable از آن بسازیم. کد زیر معادل همان تصویر است.چون Person از جنس record شده فقط از طریق کانستراکتور قابل آبجکت سازی است.اگر بخواهیم یک کلاس immutable که فقط میتوانید مقدار اولیه بگیرد (آنهم فقط به کمک کانستراکتور که برای من بسیار آزاردهنده است) میتوانیم از record استفاده کنیم. تایپ record به طور خودکار یک کلاس با پراپرتی init به ازای هر آرگومان ورودی اش می سازد و به در ساخت یک آبجکت immutable کمک میکند.با این روش جلوی مقداردهی های آتی را گرفته ایم. به همین سادگی.ترکیب تایپ Record با with بسیار زیباست.به کمک with میتوانیم یک clone از آبجکت اصلی بگیریم. به مثال زیر دقت کنید.آبجکت secondPerson یک کپی از آبجکت person است با این تفاوت که Age آن فرق میکند. نکته مهم زیر را ببینیدThe important point here is that you'll get a new instance, thus preserving the first record's immutability.نکته مهم این است که یک آبجکت جدید ساخته ایم پس خاصیت غیرقابل تغییر بودن آبجکت اول حفظ شده است.اگر از Equals برای مقایسه دو آبجکتی که از تایپ record ساخته شده اند استفاده کنیم و هر دو آبجکت مقادیر یکسان در پراپرتی ها داشته باشند حاصل true خواهد بود ولی در مثال فوق چون Age فرق کرده حاصل false است.نکته مهم زیر را ببینیدRecords will be considered equal even if they point to a different reference in memory. You can prove it by creating an object ID in the watch window while debugging.آبجکت های جنس رکورد یکسان در نظر گرفته میشوند حتا اگر به رفرنس های مختلف در حافظه اشاره کنند ولی مقادیر یکسان داشته باشند. میتوانیم این موضوع را به کمک ایجاد آبجکت آی دی در حالت دیباگ اثبات کنید.(منبع)ویژگی های جدید پایه‌ایاین ویژگی تاثیر زیادی در روش آموزش سی شارپ در آینده دارد. ما برای یک پروژه جدید کنسولی در حد Hello World مجبور بودیم ساختار شی گرای سی شارپ را حفظ کنیم. یعنی namespace جدید و کلاس program و متد main تعریف کنیم.شاید تعریف این سلسله مراتب برای یک تازه کار و علاقمند به برنامه نویسی گیج کننده باشد. مثلا سوالات زیر را بپرسدکلمه namespace یعنی چه؟کلاس چیست؟استاتیک یعنی چی؟کلمه void چرا استفاده شده؟این args چیست؟چرا قبل از args از دو براکت [] استفاده شده است؟سی شارپ در نسخه 9 به رفع گیجی بها داده است! به نظرم فرض کرده چون پایتون و جاوااسکریپت (زبان های اسکریپتی) شروع ساده تری برای نوآموزان دارد، ممکن است سی شارپ از چرخه رقابت عقب بیفتد. https://gist.github.com/5aa89c57b7ab2b3e25b784cd677eb1fc ببینید! بسیار شبیه به زبان های اسکریپتی شده. یک import ساده در بالای فایل (که در سی شارپ با using انجام میشود) و در ادامه کدهایمان را مینویسیم. حالا حتا یاد گرفتن async/await هم برای تازه کارها ساده تر خواهد بود. مثال زیر را ببینید.سی شارپ در پشت صحنه متد async برای main درست میکند تا کد ما درست کار کند.حتا از args هم میتوان استفاده کرد چون در پشت صحنه همه اینها وجود دارند.قابلیت Target typeدو کلاس به شکل زیر فرض کنید که یکی با کانستراکتور و دیگری بدون کانستراکتور باشد.برای تعریف آبجکت از هر یک از این کلاس ها مجبور نیستیم به شکل فوق عمل کنیم. کافیست عبارت زیر را بنویسیم :MyClass instance = new();یا اگر میخواهیم initializer بنویسیم به شکل زیر عمل میکنیم:MyClass instance = new() { MyValue = &quot123&quot }یا برای مقداردهی اولیه از طریق کانستراکتور میتوانیم به شکل زیر عمل کنیم:MyClassWithConstructor instance = new(&quotabc&quot);بهینه سازی ها در Pattern matchingاصلاحاتی با این عنوان از سی شارپ 7 شروع شده اند.مثلا کد زیر پیش از این به عنوان یک pattern matching بر اساس کلیدواژه is قابل استفاده بود.در مثال فوق شرط گذاشتیم که اگر آبجکت instance از جنس MySecondClass «نبود» فلان کار را بکند. به علامت تعجب دقت کنید. قبول کنید ناخواناست و ممکن است چشم تا is را میبییند خلاف آنچه نوشته شده را برداشت کند. در سی شارپ 9 این کد را به شکل زیر میتوان نوشت.خوانا و زیباحالا مثال زیر را ببینید:میتوان این مثال را در سی شارپ 9 خواناتر نوشت:کلید واژه های and و or در سی شارپ 9 به منظور خواناتر کردن pattern matching اضافه شده اند.برای مقایسه integer ها هم میتوان همین روند را پیش گرفت. تصویر زیر چیزی بود که پیش از این مینوشتیم و نسبتا خواناست.در سی شارپ جدید، میتوانیم خواناتر بنویسیم:و این روش ها را در switch هم میتوان به کار برد. در تصویر زیر به کمک if/else شرطی نوشته شده که به شکل ساده تر و بهینه تر همین شرط را با switch سنتی نیز پیاده کرده ایم و در نهایت از سوئیچی که در سی شارپ 8 معرفی شد برای پیاده سازی این شرط ها استفاده کردیم.امیدوارم این مقاله و مقاله قبل در مورد ویژگی های سی شارپ 6، 7، 8 و 9 به درد شما خورده باشد.