فصل پنجم: مرور کلی .NET 🌐

تقریباً تمام قابلیت‌های رَتایم .NET 8 از طریق یک مجموعه گسترده از نوع‌های مدیریت‌شده (managed types) در دسترس هستند. این نوع‌ها در namespaceهای سلسله‌مراتبی سازمان‌دهی شده و در مجموعه‌ای از assemblyها بسته‌بندی شده‌اند.

برخی از نوع‌های .NET مستقیماً توسط CLR استفاده می‌شوند و برای محیط میزبانی مدیریت‌شده حیاتی هستند. این نوع‌ها در assemblyای به نام System.Private.CoreLib.dll (در .NET Framework با نام mscorlib.dll) قرار دارند و شامل موارد زیر هستند:

در سطح بالاتر، نوع‌های اضافی وجود دارند که عملکرد سطح CLR را گسترش می‌دهند و ویژگی‌هایی مانند:

این مجموعه‌ها Base Class Library (BCL) را تشکیل می‌دهند.
در بالای این لایه، لایه‌های برنامه (application layers) قرار دارند که APIهایی برای توسعه انواع خاص برنامه‌ها، مانند برنامه‌های وب یا rich client ارائه می‌کنند.

در این فصل، ما موارد زیر را ارائه می‌کنیم:

چه چیزهای جدیدی در .NET 7 و .NET 8 آمده است 🚀

کتابخانه‌های پایه (Base Class Libraries) در .NET 7 و .NET 8 شامل ویژگی‌های جدید و بهبودهای عملکرد فراوانی هستند. به‌ویژه:

همچنین موتور JSON serialization بهبود یافته است و ویژگی‌های جدید و عملکرد بهتر ارائه می‌دهد. 🟦

اهداف Runtime و TFMs ⚙️

در یک فایل پروژه، عنصر <TargetFramework> تعیین می‌کند پروژه برای کدام runtime ساخته می‌شود (هدف فریمورک یا runtime target) و با یک Target Framework Moniker (TFM) مشخص می‌گردد. مقادیر معتبر شامل موارد زیر هستند:

برای مثال، این‌گونه پروژه شما روی .NET 8 هدف‌گذاری می‌شود:

<PropertyGroup>
  <TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

می‌توانید چند runtime را با استفاده از عنصر <TargetFrameworks> (جمع) هدف‌گذاری کنید، به‌طوری که هر TFM با سمیکولن (;) از دیگری جدا شود:

<TargetFrameworks>net8.0;net48</TargetFrameworks>

وقتی چند هدف تعیین می‌کنید (multitargeting)، کامپایلر برای هر هدف، یک assembly خروجی جداگانه تولید می‌کند.

هدف runtime در assembly خروجی با استفاده از ویژگی TargetFramework رمزگذاری می‌شود. یک assembly می‌تواند روی یک runtime جدیدتر (اما نه قدیمی‌تر) از هدفش اجرا شود.


.NET Standard 📚

کتابخانه‌های عمومی موجود در NuGet اگر فقط از .NET 8 پشتیبانی کنند، چندان ارزشمند نخواهند بود. هنگام نوشتن یک کتابخانه، معمولاً می‌خواهید از چندین پلتفرم و نسخه runtime پشتیبانی کنید. برای رسیدن به این هدف بدون ایجاد یک build جداگانه برای هر runtime (multitargeting)، باید کمینه مشترک (lowest common denominator) را هدف قرار دهید.

اگر فقط بخواهید از پیش‌روهای مستقیم .NET 8 پشتیبانی کنید، این کار نسبتاً ساده است: به‌عنوان مثال، اگر پروژه شما هدفش .NET 6 (net6.0) باشد، کتابخانه شما روی .NET 6، .NET 7 و .NET 8 اجرا خواهد شد.

وضعیت پیچیده‌تر می‌شود اگر بخواهید از .NET Framework یا runtimeهای قدیمی‌تر مانند Xamarin هم پشتیبانی کنید. دلیل این است که هر یک از این runtimeها یک CLR و BCL با ویژگی‌های همپوشان دارند—هیچ runtime‌ای یک زیرمجموعه کامل از دیگری نیست.

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

⚠️ .NET Standard یک runtime نیست؛ بلکه صرفاً یک مشخصه است که حداقل مجموعه‌ای از قابلیت‌ها (types و members) را تعریف می‌کند تا تضمین شود کتابخانه با مجموعه‌ای مشخص از runtimeها سازگار باشد. این مفهوم شبیه interfaces در C# است: .NET Standard مانند یک interface است که runtimeهای واقعی می‌توانند آن را پیاده‌سازی کنند.


.NET Standard 2.0 ✨

مفیدترین نسخه، .NET Standard 2.0 است. یک کتابخانه که .NET Standard 2.0 را هدف قرار می‌دهد، بدون نیاز به تغییر روی هر دو .NET مدرن (.NET 8/7/6/5 تا .NET Core 2) و .NET Framework (4.6.1+) اجرا می‌شود.

همچنین از UWP قدیمی (نسخه 10.0.16299+) و Mono 5.4+ (CLR/BCL مورد استفاده نسخه‌های قدیمی Xamarin) پشتیبانی می‌کند.

برای هدف‌گیری .NET Standard 2.0 کافی است این مورد را به فایل .csproj اضافه کنید:

<PropertyGroup>
  <TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

اکثر APIهایی که در این کتاب توضیح داده شده‌اند توسط .NET Standard 2.0 پشتیبانی می‌شوند و آن‌هایی که پشتیبانی نمی‌شوند، اغلب به‌صورت NuGet packages قابل دسترسی هستند.


دیگر نسخه‌های .NET Standard


سازگاری .NET Framework و .NET 8 🔄

به دلیل عمر طولانی .NET Framework، گاهی پیش می‌آید که کتابخانه‌هایی فقط برای .NET Framework موجود باشند (بدون معادل .NET Standard، .NET Core یا .NET 8).

برای حل این مشکل، پروژه‌های .NET 5+ و .NET Core می‌توانند به assemblies از .NET Framework ارجاع دهند، با رعایت نکات زیر:

در عمل، این روش بیشتر برای موارد ساده مانند یک assembly که یک DLL unmanaged را بسته‌بندی می‌کند، کارآمد است.


Reference Assemblies 📝

وقتی هدف شما .NET Standard است، پروژه به‌طور ضمنی به assembly‌ای به نام netstandard.dll ارجاع می‌دهد که شامل تمام types و members مجاز برای نسخه انتخابی .NET Standard شماست.

این یک reference assembly نامیده می‌شود، زیرا فقط برای کامپایلر وجود دارد و هیچ کد اجرایی ندارد.

در زمان اجرا (runtimeassemblies واقعی از طریق ویژگی‌های assembly redirection مشخص می‌شوند (انتخاب assemblyها به runtime و پلتفرمی بستگی دارد که assembly در نهایت روی آن اجرا می‌شود).

به‌طور جالب، وقتی هدف شما .NET 8 باشد نیز مشابه این اتفاق رخ می‌دهد. پروژه به‌طور ضمنی به مجموعه‌ای از reference assemblies ارجاع می‌دهد که نوع‌های آن‌ها منعکس‌کننده نوع‌های موجود در runtime assemblies نسخه .NET انتخابی است. این امر به مدیریت نسخه‌ها، سازگاری بین پلتفرم‌ها و امکان هدف‌گیری نسخه‌ای متفاوت از نسخه نصب‌شده روی دستگاه کمک می‌کند.

نسخه‌های Runtime و زبان C# 🖥️📝

به‌طور پیش‌فرض، هدف runtime پروژه تعیین می‌کند که از کدام نسخه زبان C# استفاده شود:

Conventions-UsedThis-Book

این به این دلیل است که نسخه‌های جدیدتر C# شامل ویژگی‌هایی هستند که وابسته به نوع‌هایی هستند که در runtimeهای جدیدتر معرفی شده‌اند.
شما می‌توانید نسخه زبان را در فایل پروژه با عنصر <LangVersion> بازنویسی کنید. استفاده از یک runtime قدیمی‌تر (مانند .NET Framework) با نسخه زبان جدیدتر (مانند C# 12) به این معنی است که ویژگی‌های زبان که وابسته به نوع‌های جدید .NET هستند، کار نخواهند کرد (اگرچه در برخی موارد می‌توانید این نوع‌ها را خودتان تعریف کنید یا از یک بسته NuGet وارد کنید).


CLR و BCL ⚙️📚

توجه برای فهرست‌بندی: لطفاً همه بخش‌های این قسمت را نادیده بگیرید (به جز عنوان سطح ۱). جزئیات این بخش در ادامه کتاب پوشش داده می‌شود.


نوع‌های System 🧩

مهم‌ترین نوع‌ها مستقیماً در فضای نام System قرار دارند. این نوع‌ها شامل موارد زیر هستند:

فضای نام System همچنین شامل نوع‌هایی برای انجام عملیات ریاضی (Math)، تولید اعداد تصادفی (Random) و تبدیل بین انواع مختلف (Convert و BitConverter) است.
فصل ۶ این نوع‌ها و رابط‌هایی که پروتکل‌های استاندارد در سراسر .NET را تعریف می‌کنند (مانند IFormattable برای فرمت‌بندی و IComparable برای مقایسه ترتیب) پوشش می‌دهد.

فضای نام System همچنین رابط IDisposable و کلاس GC برای تعامل با Garbage Collector را تعریف می‌کند که در فصل ۱۲ بررسی می‌شوند.


پردازش متن ✍️

فضای نام System.Text شامل کلاس StringBuilder (نسخه قابل تغییر یا mutable رشته‌ها) و نوع‌هایی برای کار با کدگذاری‌های متنی مانند UTF-8 (Encoding و زیرنوع‌های آن) است. این مبحث در فصل ۶ پوشش داده می‌شود.

فضای نام System.Text.RegularExpressions شامل نوع‌هایی برای انجام عملیات جستجو و جایگزینی پیشرفته بر اساس الگو است؛ این‌ها در فصل ۲۵ توضیح داده شده‌اند.


مجموعه‌ها 📦

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


پرس‌وجو (Querying) 🔍

Language-Integrated Query (LINQ) اجازه می‌دهد پرس‌وجوهای نوع-ایمن روی مجموعه‌های محلی و دوردست (مثلاً جداول SQL Server) انجام دهید. این مبحث در فصل‌های ۸، ۹ و ۱۰ توضیح داده شده است.
یکی از مزایای بزرگ LINQ ارائه یک API یکسان برای پرس‌وجو در حوزه‌های مختلف است. نوع‌های اصلی در فضای نام‌های زیر قرار دارند:


XML و JSON 🌐

XML و JSON به طور گسترده در .NET پشتیبانی می‌شوند.

فضاهای نام:

در مکمل آنلاین در albahari.com/nutshell، موتور سریال‌سازی JSON با ویژگی‌ها و عملکرد بهتر پوشش داده شده است.

Diagnostics 📝

در فصل ۱۳، ثبت رخدادها (Logging) و Assertion پوشش داده می‌شوند و توضیح داده می‌شود چگونه با فرآیندهای دیگر تعامل داشته باشید، در Windows Event Log بنویسید و مانیتورینگ عملکرد را مدیریت کنید. نوع‌های مرتبط در فضای نام System.Diagnostics و زیرمجموعه‌های آن تعریف شده‌اند.


Concurrency و Asynchrony ⏳

بسیاری از برنامه‌های مدرن نیاز دارند تا بیش از یک کار به‌صورت همزمان انجام شود. از C# 5.0 به بعد، این کار از طریق توابع ناهمگام (asynchronous) و سازه‌های سطح بالا مانند Taskها و Task Combinators ساده‌تر شده است. فصل ۱۴ همه این موارد را به‌طور مفصل توضیح می‌دهد، پس از شروع با اصول Multithreading.
نوع‌های مرتبط با کار با Threadها و عملیات ناهمگام در فضای نام‌های System.Threading و System.Threading.Tasks قرار دارند.


Streams و Input/Output 💾

.NET یک مدل مبتنی بر Stream برای I/O سطح پایین ارائه می‌دهد. Streamها معمولاً برای خواندن و نوشتن مستقیم روی فایل‌ها و ارتباطات شبکه استفاده می‌شوند و می‌توان آن‌ها را زنجیره‌ای یا به صورت Decorator برای افزودن قابلیت‌های فشرده‌سازی یا رمزگذاری استفاده کرد.
فصل ۱۵ معماری Stream و پشتیبانی خاص برای کار با فایل‌ها و دایرکتوری‌ها، فشرده‌سازی، Pipeها و Memory-mapped Files را توضیح می‌دهد. نوع‌های Stream و I/O در فضای نام System.IO و زیرمجموعه‌های آن تعریف شده‌اند.


Networking 🌐

شما می‌توانید به اکثر پروتکل‌های استاندارد شبکه مانند HTTP، TCP/IP، و SMTP از طریق نوع‌های فضای نام System.Net دسترسی داشته باشید. در فصل ۱۶، نشان داده می‌شود چگونه از هر یک از این پروتکل‌ها استفاده کنید، از کارهای ساده مانند دانلود از یک وب‌سایت گرفته تا استفاده مستقیم از TCP/IP برای دریافت ایمیل POP3.
فضاهای نام پوشش داده شده:


Assemblies، Reflection و Attributes 🔎

Assemblyها که برنامه‌های C# در آن‌ها کامپایل می‌شوند شامل دستورات اجرایی (IL) و متادیتا هستند که نوع‌ها، اعضا و Attributes برنامه را توصیف می‌کند. از طریق Reflection می‌توان این متادیتا را در زمان اجرا بررسی کرده و روش‌هایی مانند فراخوانی داینامیک متدها را انجام داد. با Reflection.Emit می‌توان کد جدید را به‌صورت پویا ساخت.

در فصل ۱۷، ترکیب Assemblyها و نحوه لود کردن و جداسازی داینامیک آن‌ها توضیح داده شده است. در فصل ۱۸، Reflection و Attributes بررسی می‌شوند—چگونگی بررسی متادیتا، فراخوانی داینامیک توابع، نوشتن Attributes سفارشی، Emit نوع‌های جدید و تحلیل IL خام.
نوع‌های مرتبط در فضاهای نام زیر قرار دارند:


Dynamic Programming 🌀

در فصل ۱۹، برخی الگوها برای برنامه‌نویسی داینامیک و استفاده از Dynamic Language Runtime (DLR) بررسی می‌شوند. توضیح داده می‌شود چگونه Visitor Pattern پیاده‌سازی شود، اشیای داینامیک سفارشی نوشته شوند و با IronPython تعامل داشته باشیم. نوع‌های مرتبط در فضای نام System.Dynamic قرار دارند.


Cryptography 🔐

.NET پشتیبانی گسترده‌ای برای الگوریتم‌های Hashing و رمزگذاری ارائه می‌دهد. فصل ۲۰ شامل موضوعاتی مانند Hashing، رمزگذاری متقارن و کلید عمومی، و Windows Data Protection API است. نوع‌های مرتبط در:


Advanced Threading 🧵

توابع ناهمگام C# برنامه‌نویسی همزمان را بسیار آسان‌تر می‌کنند، زیرا نیاز به تکنیک‌های سطح پایین را کاهش می‌دهند. با این حال، هنوز مواقعی وجود دارد که به Signaling Constructs، Thread-local Storage، Reader/Writer Locks و غیره نیاز است. فصل ۲۱ این موارد را به‌طور عمیق توضیح می‌دهد. نوع‌های Threading در System.Threading قرار دارند.


Parallel Programming ⚡

در فصل ۲۲، کتابخانه‌ها و نوع‌های مربوط به بهره‌گیری از پردازنده‌های چند هسته‌ای بررسی می‌شوند، شامل Task Parallelism، Imperative Data Parallelism، و Functional Parallelism (PLINQ).


Span و Memory 🧩

برای کمک به بهینه‌سازی عملکرد در نقاط حساس حافظه (micro-optimizing hotspots)، CLR چند نوع ارائه می‌دهد که به شما کمک می‌کنند برنامه‌ای بنویسید که بار روی Memory Manager را کاهش دهد. دو نوع کلیدی Span و Memory هستند که در فصل ۲۳ توضیح داده می‌شوند.

Native و COM Interoperability 🖥️

شما می‌توانید با هر دو نوع کد Native و Component Object Model (COM) تعامل داشته باشید.


Regular Expressions 🔍

در فصل ۲۵، توضیح داده می‌شود چگونه می‌توان از Regular Expressions برای مطابقت الگوهای کاراکتری در رشته‌ها استفاده کرد.


Serialization 💾

.NET چندین سیستم برای ذخیره و بازیابی اشیاء به صورت باینری یا متنی ارائه می‌دهد. این سیستم‌ها می‌توانند برای ارتباط بین برنامه‌ها و همچنین ذخیره و بازیابی اشیاء روی فایل استفاده شوند.
در مکمل آنلاین در این لینک تمام چهار موتور Serialization پوشش داده شده‌اند:


Roslyn Compiler ⚙️

کامپایلر C# خودش در C# نوشته شده است—پروژه آن به نام Roslyn شناخته می‌شود و کتابخانه‌های آن به صورت NuGet Packages در دسترس هستند.
با این کتابخانه‌ها، می‌توان از عملکرد کامپایلر به روش‌های مختلفی استفاده کرد، نه تنها برای کامپایل سورس کد به Assembly، بلکه برای نوشتن ابزارهای تحلیل کد و Refactoring نیز.
Roslyn در مکمل آنلاین در این لینک پوشش داده شده است.


Application Layers 🏗️

برنامه‌های مبتنی بر UI می‌توانند به دو دسته تقسیم شوند:

Thin-Client با C#

برای نوشتن برنامه‌های Thin Client در C#، از ASP.NET Core استفاده می‌کنیم که روی Windows، Linux و macOS اجرا می‌شود. ASP.NET Core همچنین برای نوشتن Web API طراحی شده است.

Rich-Client

برای برنامه‌های Rich Client، انتخاب‌های مختلفی وجود دارد:


ASP.NET Core 🌐

ASP.NET Core جانشین سبک و مدولار ASP.NET است و مناسب برای ساخت وب‌سایت‌ها، Web APIهای مبتنی بر REST و Microservices می‌باشد. همچنین می‌تواند با دو فریم‌ورک محبوب Single-Page Application یعنی React و Angular ترکیب شود.

ویژگی‌ها:

ASP.NET Core روی Windows، Linux و macOS اجرا می‌شود و می‌تواند به‌صورت Self-host در یک Process دلخواه اجرا شود. برخلاف ASP.NET قدیمی در .NET Framework، ASP.NET Core به System.Web و Legacy Web Forms وابسته نیست.

مزایای Thin-Client نسبت به Rich Client:


Windows Desktop 🖥️

لایه Windows Desktop دو API UI برای نوشتن برنامه‌های Rich Client ارائه می‌دهد:

WPF 🎨

WPF در سال ۲۰۰۶ معرفی شد و از آن زمان به‌طور مداوم بهبود یافته است. برخلاف Windows Forms، WPF کنترل‌ها را به‌صورت صریح با DirectX رندر می‌کند که مزایای زیر را دارد:

یادگیری WPF به دلیل اندازه و پیچیدگی آن زمان‌بر است. نوع‌های لازم برای نوشتن برنامه‌های WPF در System.Windows namespace و زیرمجموعه‌های آن به جز System.Windows.Forms قرار دارند.


Windows Forms 🖥️

Windows Forms یک API Rich Client است که با اولین نسخه .NET Framework در سال ۲۰۰۰ منتشر شد. نسبت به WPF، Windows Forms فناوری ساده‌تری است که اکثر ویژگی‌های مورد نیاز برای نوشتن یک برنامه معمولی Windows را ارائه می‌دهد و همچنان در نگهداری برنامه‌های Legacy کاربرد دارد.

اما در مقایسه با WPF، محدودیت‌های زیادی دارد که بیشتر ناشی از Wrapper بودن روی GDI+ و کتابخانه کنترل‌های Win32 است:

مزیت مثبت Windows Forms این است که نسبتاً ساده است و یادگیری آن آسان است و هنوز تعداد مناسبی کنترل شخص ثالث دارد.
نوع‌های Windows Forms در System.Windows.Forms (در System.Windows.Forms.dll) و System.Drawing (در System.Drawing.dll) قرار دارند. فضای نام دوم همچنین شامل نوع‌های GDI+ برای رسم کنترل‌های سفارشی است.


UWP و WinUI 3 📱

UWP یک API Rich Client برای نوشتن UIهای Touch-First است که روی Windows 10+ و دستگاه‌ها اجرا می‌شود. واژه "Universal" به توانایی اجرای آن روی دستگاه‌های مختلف Windows 10 مانند Xbox، Surface Hub، HoloLens و Windows Phone اشاره دارد.

UWP از XAML استفاده می‌کند و شباهت‌هایی با WPF دارد، اما تفاوت‌های کلیدی آن عبارتند از:

به دلیل این محدودیت‌ها، UWP نتوانست به محبوبیت WPF و Windows Forms برسد. برای حل این مشکل، مایکروسافت UWP را به فناوری جدیدی به نام Windows App SDK تبدیل کرده است، با لایه UI به نام WinUI 3.

ویژگی‌های Windows App SDK:

با این حال، WinUI 3 هنوز به محبوبیت گسترده APIهای کلاسیک Windows Desktop نرسیده است و Windows App SDK در زمان نگارش از Xbox و HoloLens پشتیبانی نمی‌کند و نیازمند دانلود جداگانه برای کاربر است.


MAUI 🌐📱

MAUI (سابقاً Xamarin) امکان توسعه برنامه‌های موبایل با C# برای iOS و Android را فراهم می‌کند و همچنین می‌توان برنامه‌های Cross-Platform دسکتاپ برای macOS و Windows نوشت (via Catalyst و Windows App SDK).

Runtime/BCL که روی iOS و Android اجرا می‌شود Mono نام دارد (نسخه‌ای مشتق‌شده از Mono Runtime متن‌باز). تاریخی، Mono با .NET کاملاً سازگار نبود و کتابخانه‌هایی که روی هر دو اجرا می‌شدند، معمولاً .NET Standard هدف قرار می‌گرفتند. از .NET 6 به بعد، Interface عمومی Mono با .NET ادغام شد و Mono در واقع به‌عنوان یک پیاده‌سازی از .NET درآمد.

MAUI شامل رابط پروژه یکپارچه، Hot Reloading، و پشتیبانی از Blazor Desktop و Hybrid Apps است. جزئیات بیشتر در صفحه رسمی MAUI موجود است.