مقدمه

متغیرها در C# مثل هر زبان دیگر، برای ذخیره‌ی مقادیر کاربرد دارند. تعریف یک متغیر در C# باید با اعلان نوع (type declaration) همراه باشد. البته در مواردی که نوع متغیر مشخص نباشد، می‌توان از کلمه کلیدی var به جای نوع متغیر استفاده کرد. یک کلمه کلیدی دیگر با نام dynamic هم وجود دارد که این امکان را فراهم می‌کند که متغیری تعریف کنیم که نوعش بتواند در طول برنامه تغییر کند. در این درس، این موضوعات و سایر مطالب مربوط به تعریف و استفاده از متغیرهای C# بیان خواهد شد. اما قبل از آن، باید مختصری در مورد سیستم نوع (type system) در .NET و C# صحبت کنیم.

سیستم نوع در C# و .NET

سیستم نوع یا Type System یک مفهوم کلیدی در برنامه‌نویسی است که ماهیت یک زبان را تا حد زیادی روشن می‌کند. دینامیک یا استاتیک بودن یک زبان، موضوعی است که از روی سیستم نوع آن زبان مشخص می‌شود. همچنین، این سیستم نوع است که نوع‌های داده‌ای قابل استفاده در زبان را تعیین می‌کند. در C# با توجه به معماری چندزبانه‌ی فریمورک .NET ما به جای Type System با Common Type System مواجه هستیم؛ یعنی یک سیستم نوع مشترک بین چند زبان.

در فصل اول در مورد استانداردهای CTS و CLS و نقش آنها در فریمورک .NET صحبت کردیم. برای یادآوری عرض می‌کنم که CTS شامل تعریف مجموعه‌ای از قوانین و دستورالعمل‌هاست که سازگاری نوع‌های داده را در زبان‌های مختلف .NET تضمین می‌کند و CLS زیرمجموعه‌ای از CTS است که سازگاری کد بین زبان‌های مختلف .NET را تضمین می‌کند. کدی که فقط متکی به ویژگی‌های تعریف‌شده در CLS باشد، اصطلاحاً CLS-compliant یا سازگار با CLS گفته می‌شود و چنین کدی را می‌توان در هر یک از زبان‌های .NET توسعه داد.

مهمترین وظیفه‌ی CTS تعریف نوع‌های داده است. باز هم از فصل اول یادآوری می‌کنم که یک نوع داده (datatype) که نوع (type) هم گفته می‌شود، می‌تواند یک کلاس (class)، ساختار (struct)، اینترفیس (interface)، شمارشی (enum) و یا نماینده (delegate) باشد. برای مثال، System.Int32 که در C# می‌توان با کلمه کلیدی int نیز به آن اشاره کرد، یک ساختار است و System.String که با استفاده از کلمه کلیدی string نیز می‌توان به آن ارجاع داد، یک کلاس است. نوع‌هایی مانند Int32 و String که در فضای نام System تعریف شده‌اند و عبارات مستعاری برای دسترسی به آنها تعریف شده، نوع‌های Primitive یا مقدماتی نامیده می‌شوند که در درس بعد، آنها را مورد بررسی قرار می‌دهیم.

سیستم نوع استاتیک

C# یک زبان استاتیک یا به بیان دقیق‌تر، یک زبان Statically-typed است. این یعنی اینکه وقتی نوع یک متغیر C# مشخص شد، امکان تغییر آن در طول برنامه وجود ندارد. بنابراین، کد زیر منجر به بروز خطا خواهد شد.

Copy Icon Program.cs
int x = 8;
x = "eight";

در محیط vscode این کد باعث نمایش پیغام خطای زیر می‌شود.

Cannot implicitly convert type 'string' to 'int'

برای درک کامل این پیغام، باید تا درس بعد صبر کنید اما فعلاً همینقدر بدانید که این خطا به این دلیل رخ داده که ما سعی کرده‌ایم یک مقدار از نوع string را به متغیری از نوع int بدهیم.

البته در این میان، یک تبصره هم وجود دارد و آن این است که با استفاده از کلمه کلیدی dynamic می‌توانیم متغیرهایی تعریف کنیم که نوعشان در طول برنامه می‌تواند تغییر کند. در این مورد، بعداً‌ بیشتر صحبت می‌کنیم.

تعریف متغیر به روش صریح

روش اصلی تعریف متغیر در C# روش صریح است که در آن از type declaration برای اعلان صریح نوع متغیر استفاده می‌شود. در این روش، ابتدا نوع متغیر و سپس نام آن آورده می‌شود. همه‌ی متغیرهایی که تا الان در مثال‌ها دیده‌ایم، به روش صریح تعریف شده بودند.

Copy Icon Program.cs
int i = 8;
int j;
j = 6;
bool b = false;

این کد شامل تعریف و مقداردهی سه متغیر است که دو تای آنها از نوع int و یکی از نوع bool هستند. همانطور که می‌بینید، تعریف و مقداردهی می‌تواند مانند متغیرهای i و b در قالب یک گزاره انجام شود و یا مانند متغیر j در دو گزاره‌ی مجزا صورت گیرد. اعطای یک مقدار اولیه به متغیر را initialization می‌گویند و متغیری که تعریف شده اما هنوز مقداری دریافت نکرده، یک متغیر uninitialized است.

تعریف متغیر به روش ضمنی

در اغلب موارد، ما متغیرها را به روش صریح تعریف می‌کنیم تا نوع آنها برای کامپایلر مشخص شود اما گاهی اوقات ممکن است نوع یک متغیر در زمان تعریف آن مشخص نباشد. فعلاً دنبال مثالی از این وضع نباشید و فقط بپذیرید که چنین امکانی وجود دارد. در اینگونه موارد، می‌توانیم از کلمه کلیدی var برای تعریف متغیر استفاده کنیم. با این کار، تعیین نوع متغیر به کامپایلر واگذار می‌شود و کامپایلر از روی اولین مقداری که در متغیر قرار می‌گیرد، نوعش را مشخص می‌کند. متغیری که با استفاده از var تعریف شود یک متغیر دارای نوع ضمنی یا Implicitly-typed نامیده می‌شود.

Copy Icon Program.cs
var i = 8;
//var j;
//j = 6;
var b = false;

در اینجا متغیرهای i و b را که بالاتر به صورت صریح تعریف کرده بودیم، به صورت ضمنی و با استفاده از var تعریف کرده‌ایم. البته همانطور که می‌بینید، دو خط مربوط به تعریف و مقداردهی متغیر j را کامنت کرده‌ایم. اگر این خطوط را از حالت کامنت خارج کنیم، با پیغام خطای زیر مواجه می‌شویم.

Implicitly-typed variables must be initialized.

این پیغام به ما می‌گوید که متغیرهای دارای نوع ضمنی باید همزمان با تعریف، مقداردهی شوند. پس، به خاطر داشته باشید که تعریف و مقداردهی متغیرهای دارای نوع ضمنی نمی‌تواند در دو گزاره‌ی مجزا انجام شود. علاوه بر این، var فقط برای متغیرهای درون بلاک یک متد (یا پراپرتی) قابل استفاده است و نمی‌توان از آن برای تعیین نوع بازگشتی متدها یا برای تعریف فیلدهای یک کلاس استفاده کرد. فراموش نکنید که گزاره‌های Top-level فقط ظاهراً درون هیچ متدی نیستند اما در واقع، به بلاک یک متد Entry point متعلق هستند که ما آن را نمی‌بینیم.

اگر بخواهیم خیلی دقیق باشیم، باید از اطلاق عنوان کلمه کلیدی (keyword) به var خودداری کنیم. چون همانطور که می‌دانید، از یک کلمه کلیدی نمی‌توانیم به عنوان نام یک متغیر یا متد استفاده کنیم اما برای var چنین منعی وجود ندارد. یعنی گزاره‌ای مثل int var = 8; مجاز است و منجر به گزارش خطا نمی‌شود.

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

Copy Icon Program.cs
var x = 8;
x = 10;
            
x = "Hi";

تعریف متغیرهای دینامیک

چیزی که مثال بالا می‌خواهد این است که x یک متغیر دینامیک باشد و نتیجتاً بتواند در طول برنامه مقادیری از نوع‌های مختلف را دریافت کند. این کاری است که توسط کلمه کلیدی dynamic انجام می‌شود.

Copy Icon Program.cs
dynamic x = 8;
x = 10;
            
x = "Hi";

متغیری که با استفاده از dynamic تغریف شود، در زمان اجرا (و نه در زمان کامپایل) ارزیابی می‌شود. بنابراین، چون کامپایلر نیازی به دانستن نوع این متغیر ندارد، نوع آن می‌تواند تغییر کند و در هر لحظه دارای یک نوع باشد. در فصل هفدهم در مورد جزئیات مربوط به کلید واژه‌ی dynamic و برنامه‌نویسی دینامیک صحبت خواهیم کرد.