مقدمه
در درس قبل، با تفاوت بنیادین بین نوعهای مقداری و ارجاعی آشنا شدیم. یک نکتهی کلیدی این بود که
نوعهای مقداری نمیتوانند مقدار null (پوچ یا بیمقدار) را بپذیرند و باید همیشه یک مقدار مشخص داشته
باشند. اما در دنیای واقعی برنامهنویسی، سناریوهای زیادی وجود دارد که در آنها یک مقدار ممکن است
وجود نداشته باشد. برای مثال، سن یک کاربر در پایگاه داده ممکن است وارد نشده باشد، یا یک فیلد
اختیاری در فرم، خالی گذاشته شود. برای حل این مشکل، C# مفهومی به نام نوعهای
مقداری پوچپذیر (Nullable Value Types) را معرفی کرده است که به ما اجازه میدهد
مقدار null را به متغیرهای از نوع مقداری اختصاص دهیم.
مشکل: نوعهای مقداری نمیتوانند null باشند
بیایید ابتدا مشکل را با یک مثال ساده مرور کنیم. همانطور که میدانیم، نوعهای ارجاعی مانند string میتوانند به سادگی مقدار null
بگیرند، اما تلاش برای انجام همین کار با یک نوع مقداری مانند int
منجر به خطای کامپایل (compile-time error) میشود.
Program.cs
string name = null;
int age = null;
این محدودیت، طراحی زبان C# را امنتر میکند، زیرا جلوی بسیاری از خطاهای ناشی از
مقادیر null را که در زبانهای دیگر رایج است، میگیرد. اما برای
موقعیتهایی که "نبود مقدار" یک حالت معتبر است، به یک راهحل نیاز داریم. اینجاست که نوعهای
پوچپذیر وارد عمل میشوند.
نوعهای مقداری پوچپذیر (Nullable Value Types)
یک نوع پوچپذیر، در واقع یک ساختار (struct) ویژه است که یک نوع
مقداری را در بر میگیرد و به آن اجازه میدهد علاوه بر مقادیر معتبر خود، مقدار null را نیز بپذیرد.
نحوهی تعریف یک نوع پوچپذیر
برای تعریف یک نوع مقداری به عنوان پوچپذیر، کافی است یک علامت سؤال (?) بعد از نام نوع قرار دهیم. این روش، یک سینتکس کوتاهشده برای نوع
ژنریک System.Nullable<T> است.
Program.cs
int? age = null;
age = 30;
Nullable<bool> hasAgreed = null;
Console.WriteLine($"The age is: {age}");
Console.WriteLine($"Has agreed: {hasAgreed}");
در کد بالا، متغیر age از نوع int? (بخوانید "int پوچپذیر") تعریف
شده است. این متغیر اکنون میتواند یک عدد صحیح یا مقدار null را در
خود نگه دارد. در عمل، همیشه از سینتکس کوتاهتر `?` استفاده میشود زیرا خواناتر و رایجتر است.
کار با نوعهای پوچپذیر
وقتی با یک متغیر پوچپذیر کار میکنیم، قبل از استفاده از مقدار آن باید بررسی کنیم که آیا واقعاً
مقداری دارد یا null است. در غیر این صورت، ممکن است با خطای زمان
اجرا مواجه شویم. نوعهای پوچپذیر دو پراپرتی بسیار مفید برای این کار ارائه میدهند: HasValue و Value.
بررسی کردن مقدار و دسترسی به آن
پراپرتی HasValue یک مقدار بولین برمیگرداند. اگر متغیر پوچپذیر
مقداری داشته باشد، این پراپرتی true و در غیر این صورت false خواهد بود. اگر و فقط اگر HasValue
برابر با true باشد، میتوانیم با استفاده از پراپرتی Value به مقدار اصلی آن دسترسی پیدا کنیم.
Program.cs
int? temperature = 22;
if (temperature.HasValue)
{
Console.WriteLine($"Current temperature is: {temperature.Value}°C");
}
else
{
Console.WriteLine("Temperature data is not available.");
}
if (temperature != null)
{
Console.WriteLine("It's definitely not null.");
}
همانطور که در مثال میبینید، الگوی رایج این است که ابتدا با HasValue (یا مقایسه مستقیم با null) از
وجود مقدار اطمینان حاصل کرده و سپس با Value از آن استفاده کنیم.
تلاش برای دسترسی به Value زمانی که متغیر null است، منجر به خطای InvalidOperationException میشود.
عملگر ادغام پوچ (Null-Coalescing Operator: ??)
بررسی با if کاملاً کارآمد است، اما C# یک راهکار بسیار
کوتاهتر و خواناتر برای کار با مقادیر پوچپذیر ارائه میدهد: عملگر ادغام پوچ
`??`. این عملگر دو عملوند میگیرد. اگر عملوند اول null
نباشد، مقدار آن را برمیگرداند. در غیر این صورت (اگر null باشد)،
مقدار عملوند دوم را برمیگرداند. این عملگر برای تعیین یک مقدار پیشفرض عالی است.
Program.cs
int? downloadedFiles = null;
int filesToProcess = downloadedFiles ?? 0;
Console.WriteLine($"Files to process: {filesToProcess}");
downloadedFiles = 15;
filesToProcess = downloadedFiles ?? 0;
Console.WriteLine($"Files to process: {filesToProcess}");
در این کد، downloadedFiles ?? 0 میگوید: "اگر downloadedFiles مقدار دارد، از همان استفاده
کن؛ وگرنه از 0 استفاده کن". این کد معادل یک بلوک if-else کامل است
اما بسیار مختصرتر و خواناتر نوشته شده است.
نوعهای ارجاعی پوچپذیر (Nullable Reference Types)
از نسخه 8 C# به بعد، مفهوم پوچپذیری به نوعهای ارجاعی نیز گسترش یافت. در
پروژههای مدرن .NET، قابلیتی به نام Nullable Reference Types به
صورت پیشفرض فعال است. این قابلیت، رفتار پیشفرض نوعهای ارجاعی را تغییر میدهد. قبل از این
قابلیت، هر متغیر از نوع ارجاعی (مثل string) میتوانست null باشد. اما با فعال بودن این ویژگی، کامپایلر فرض میکند که یک
string معمولی نباید null باشد و در
صورت احتمال وجود null، به شما هشدار میدهد.
برای اینکه به کامپایلر بگویید یک نوع ارجاعی میتواند null باشد، باید از همان سینتکس علامت سؤال استفاده کنید.
string message = "Hello";
string? optionalMessage = null;
Console.WriteLine(optionalMessage.Length);
این ویژگی یک ابزار قدرتمند برای جلوگیری از خطای رایج NullReferenceException در زمان کامپایل است و شما را تشویق میکند
که کدهای امنتری بنویسید.