یک بیت یا یک بایت؟ مسئله این است!
از نظر تئوری، برای ذخیره یک مقدار bool (true یا false) فقط یک بیت حافظه کافیه. اما در عمل، CLR برای بهینگی و راحتی کار با پردازنده، برای هر متغیر bool یک بایت (۸ بیت) کامل در حافظه در نظر میگیره.
نکته حرفهای:
اگه نیاز دارید تعداد خیلی خیلی زیادی bool رو در حافظه ذخیره کنید و فضا براتون مهمه (مثلاً در کار با فایلهای باینری)، میتونید از کلاس System.Collections.BitArray استفاده کنید که برای هر مقدار، دقیقاً یک بیت مصرف میکنه.
bool فقط یک نام مستعاره!
مثل بقیه انواع داده پیشساخته، کلمه کلیدی bool در #C فقط یک نام مستعار (alias) ساده و راحت برای نوع داده اصلی در فریمورک داتنت یعنی System.Boolean هست.
این دو خط کد، در نهایت یک کار یکسان رو انجام میدن:
bool b1 = true;
System.Boolean b2 = true; // این همون بالاییه، فقط با اسم کاملش!
حرف آخر:
پس یادتون باشه: bool یه نوع منطقی خالصه، نه یه عدد. در حافظه یه بایت جا میگیره و فقط یه نام مستعار برای System.Boolean هست!
🔖 هشتگها :
#CSharp
#QuickTip
#DotNet
#Boolean
#CodingTips
🔒 رازهای string در #C :
همهی ما هر روز با string کار میکنیم، ولی دو تا از بزرگترین رازهای رفتاریش رو میدونید؟ stringها در سیشارپ هم تغییرناپذیرن و هم اپراتور == روشون یه رفتار خاص داره.
درک این دو نکته، دید شما رو به زبان #C و نحوه مدیریت حافظه، عمیقتر میکنه و شما رو به یه توسعهدهنده مسلطتر تبدیل میکنه.
1️⃣ راز اول:
وقتی یه string میسازید، دیگه هرگز نمیتونید محتوای اون رو در حافظه تغییر بدید. هر عملیاتی که به نظر میاد داره یه string رو عوض میکنه (مثل ToUpper() یا +)، در واقع داره یه string کاملاً جدید در حافظه میسازه و قبلی رو دستنخورده رها میکنه.
مثال:
چرا این ویژگی خوبه؟
این ویژگی باعث میشه کد شما قابل پیشبینیتر، امنتر (به خصوص در محیطهای چندنخی یا Multi-threaded) و بهینهتر باشه، چون کامپایلر میتونه رشتههای یکسان رو در حافظه به اشتراک بذاره (به این کار String Interning میگن).
2️⃣راز دوم:
تو پست "تلههای برابری" دیدیم که == روی Reference Typeها، آدرس حافظه رو مقایسه میکنه. string هم یه Reference Type هست، پس چرا این کد true برمیگردونه؟
جواب:
چون تیم طراح #C اپراتور == رو برای string به صورت خاص بازنویسی (Overload) کرده. اونها میدونستن که ۹۹٪ مواقع وقتی دو تا رشته رو مقایسه میکنیم، منظورمون مقایسه محتوای اونهاست، نه آدرس حافظهشون. پس این اپراتور رو هوشمند کردن تا کار ما رو راحتتر کنن و جلوی یه باگ رایج رو بگیرن!
این یکی از نمونههای طراحی هوشمندانه در زبان #C هست.
🤔 حرف حساب و تجربه شما
این رفتارهای خاص string، نمونهای عالی از طراحی هوشمندانه در زبان #C هست که هم به کارایی کمک میکنه و هم جلوی خطاهای رایج رو میگیره.
آیا تا حالا به این فکر کرده بودید که وقتی یه رشته رو با + ترکیب میکنید، در واقع دارید رشتههای جدید در حافظه میسازید (که میتونه روی پرفورمنس تاثیر بذاره)؟ یا از رفتار خاص == برای string خبر داشتید؟
تغییرناپذیری (Immutability) و جادوی برابری
همهی ما هر روز با string کار میکنیم، ولی دو تا از بزرگترین رازهای رفتاریش رو میدونید؟ stringها در سیشارپ هم تغییرناپذیرن و هم اپراتور == روشون یه رفتار خاص داره.
درک این دو نکته، دید شما رو به زبان #C و نحوه مدیریت حافظه، عمیقتر میکنه و شما رو به یه توسعهدهنده مسلطتر تبدیل میکنه.
1️⃣ راز اول:
🛡تغییرناپذیری (Immutability):
وقتی یه string میسازید، دیگه هرگز نمیتونید محتوای اون رو در حافظه تغییر بدید. هر عملیاتی که به نظر میاد داره یه string رو عوض میکنه (مثل ToUpper() یا +)، در واقع داره یه string کاملاً جدید در حافظه میسازه و قبلی رو دستنخورده رها میکنه.
مثال:
string s1 = "hello";
// این کد s1 را تغییر نمیدهد!
// بلکه یک رشته جدید ("HELLO") میسازد و در s2 قرار میدهد.
string s2 = s1.ToUpper();
Console.WriteLine($"s1: {s1}"); // خروجی: s1: hello
Console.WriteLine($"s2: {s2}"); // خروجی: s2: HELLO
چرا این ویژگی خوبه؟
این ویژگی باعث میشه کد شما قابل پیشبینیتر، امنتر (به خصوص در محیطهای چندنخی یا Multi-threaded) و بهینهتر باشه، چون کامپایلر میتونه رشتههای یکسان رو در حافظه به اشتراک بذاره (به این کار String Interning میگن).
2️⃣راز دوم:
🎭جادوی برابری (==)
تو پست "تلههای برابری" دیدیم که == روی Reference Typeها، آدرس حافظه رو مقایسه میکنه. string هم یه Reference Type هست، پس چرا این کد true برمیگردونه؟
string a = "test";
string b = "test";
Console.WriteLine(a == b); // خروجی: True! ولی چرا؟
جواب:
چون تیم طراح #C اپراتور == رو برای string به صورت خاص بازنویسی (Overload) کرده. اونها میدونستن که ۹۹٪ مواقع وقتی دو تا رشته رو مقایسه میکنیم، منظورمون مقایسه محتوای اونهاست، نه آدرس حافظهشون. پس این اپراتور رو هوشمند کردن تا کار ما رو راحتتر کنن و جلوی یه باگ رایج رو بگیرن!
این یکی از نمونههای طراحی هوشمندانه در زبان #C هست.
🤔 حرف حساب و تجربه شما
این رفتارهای خاص string، نمونهای عالی از طراحی هوشمندانه در زبان #C هست که هم به کارایی کمک میکنه و هم جلوی خطاهای رایج رو میگیره.
آیا تا حالا به این فکر کرده بودید که وقتی یه رشته رو با + ترکیب میکنید، در واقع دارید رشتههای جدید در حافظه میسازید (که میتونه روی پرفورمنس تاثیر بذاره)؟ یا از رفتار خاص == برای string خبر داشتید؟
🔖 هشتگها :
#CSharp
#String
#BestPractices
🛠 نوشتن string مثل یک حرفهای:
در سیشارپ، فقط یک راه برای نوشتن رشتهها وجود نداره. ابزارهای مختلفی در اختیار ماست که هر کدوم برای یه سناریوی خاص، بهترین انتخابه. تسلط بر این ابزارها، کد شما رو نه تنها خواناتر، که حرفهایتر هم میکنه.
1️⃣ روش کلاسیک: دردسرهای Escape Sequence (\)
این روش سنتیترین راهه. برای رشتههای ساده خوبه، ولی وقتی پای کاراکترهای خاص مثل بکاسلش (\) یا کوتیشن (") وسط بیاد، کد شما به سرعت زشت و ناخوانا میشه.
به خصوص برای مسیر فایلها در ویندوز، این روش یه کابوس واقعیه!
2️⃣ راه حل تمیز: Verbatim Literals (@)
اینجاست که @ مثل یه قهرمان وارد میشه! با گذاشتن یه @ قبل از رشته، شما به کامپایلر میگید: "هرچیزی که داخل این رشته هست رو دقیقاً همونطور که هست در نظر بگیر و هیچ کاراکتری رو escape نکن."
این قابلیت دو تا مزیت بزرگ داره:
مسیرهای خوانا:
متنهای چند خطی:
میتونید به راحتی و بدون نیاز به \n، متنهای چند خطی بنویسید.
3️⃣ قدرت مدرن: Raw String Literals ("""...""") (از C# 11 به بعد)
این جدیدترین و قدرتمندترین ابزار ماست که برای حل مشکلات کار با JSON، XML، HTML یا هر متن پیچیدهای طراحی شده.
رشته رو بین سه تا (یا بیشتر) دابل کوتیشن میذارید و دیگه هیچ نیازی به هیچگونه escape کردنی ندارید. حتی لازم نیست " رو دو بار بنویسید!
این قابلیت، کد شما رو به شکل چشمگیری تمیزتر و قابل نگهداریتر میکنه، به خصوص وقتی با متنهای ساختاریافته سر و کار دارید.
🤔 حرف حساب و ابزار شما
هر کدوم از این ابزارها، برای یه کاری ساخته شدن.
●برای رشتههای ساده ⟵ روش کلاسیک
●برای مسیر فایل و متنهای چند خطی ⟵ Verbatim (@)
●برای JSON، XML و متنهای پیچیده ⟵ Raw String (""")
شما از کدوم یکی از این روشها بیشتر استفاده میکنید؟ آیا از طرفدارای Raw String Literals جدید هستید یا هنوز با @ کارتون راه میفته؟
در سیشارپ، فقط یک راه برای نوشتن رشتهها وجود نداره. ابزارهای مختلفی در اختیار ماست که هر کدوم برای یه سناریوی خاص، بهترین انتخابه. تسلط بر این ابزارها، کد شما رو نه تنها خواناتر، که حرفهایتر هم میکنه.
1️⃣ روش کلاسیک: دردسرهای Escape Sequence (\)
این روش سنتیترین راهه. برای رشتههای ساده خوبه، ولی وقتی پای کاراکترهای خاص مثل بکاسلش (\) یا کوتیشن (") وسط بیاد، کد شما به سرعت زشت و ناخوانا میشه.
به خصوص برای مسیر فایلها در ویندوز، این روش یه کابوس واقعیه!
// هر \ باید دو بار نوشته بشه!
string classicPath = "C:\\Users\\MyUser\\Documents\\file.txt";
2️⃣ راه حل تمیز: Verbatim Literals (@)
اینجاست که @ مثل یه قهرمان وارد میشه! با گذاشتن یه @ قبل از رشته، شما به کامپایلر میگید: "هرچیزی که داخل این رشته هست رو دقیقاً همونطور که هست در نظر بگیر و هیچ کاراکتری رو escape نکن."
این قابلیت دو تا مزیت بزرگ داره:
مسیرهای خوانا:
// همون مسیر فایل، ولی تمیز و خوانا!
string verbatimPath = @"C:\Users\MyUser\Documents\file.txt";
متنهای چند خطی:
میتونید به راحتی و بدون نیاز به \n، متنهای چند خطی بنویسید.
string multiLine = @"این
یک متن
چند خطی است.";
نکته: برای استفاده از " داخل رشته @، کافیه دو بار بنویسیدش:
@"<tag id=""123"">"
3️⃣ قدرت مدرن: Raw String Literals ("""...""") (از C# 11 به بعد)
این جدیدترین و قدرتمندترین ابزار ماست که برای حل مشکلات کار با JSON، XML، HTML یا هر متن پیچیدهای طراحی شده.
رشته رو بین سه تا (یا بیشتر) دابل کوتیشن میذارید و دیگه هیچ نیازی به هیچگونه escape کردنی ندارید. حتی لازم نیست " رو دو بار بنویسید!
string json = """
{
"Name" : "C# Geeks",
"Quote" : "We write clean code!",
"Path" : "C:\Temp\Data.json"
}
""";
این قابلیت، کد شما رو به شکل چشمگیری تمیزتر و قابل نگهداریتر میکنه، به خصوص وقتی با متنهای ساختاریافته سر و کار دارید.
🤔 حرف حساب و ابزار شما
هر کدوم از این ابزارها، برای یه کاری ساخته شدن.
●برای رشتههای ساده ⟵ روش کلاسیک
●برای مسیر فایل و متنهای چند خطی ⟵ Verbatim (@)
●برای JSON، XML و متنهای پیچیده ⟵ Raw String (""")
شما از کدوم یکی از این روشها بیشتر استفاده میکنید؟ آیا از طرفدارای Raw String Literals جدید هستید یا هنوز با @ کارتون راه میفته؟
🔖 هشتگها :
#CSharp
#String
#CodingTips
✨ ساختن رشتهها در #C :
همهی ما یه زمانی اینجوری رشته میساختیم:
این کد کار میکنه، ولی آیا بهترین، خواناترین و بهینهترین راهه؟
بیاید دو روش اصلی ساختن رشته در سیشارپ رو با تمام جزئیاتشون کالبدشکافی کنیم و ببینیم کی باید از کدوم استفاده کرد.
🔗 روش سنتی: الحاق با + و تلهی پرفورمنس
این سادهترین روشه. اپراتور + دو تا رشته رو به هم میچسبونه. اگه یکی از مقادیر رشته نباشه، #C به صورت خودکار متد ()ToString رو روش صدا میزنه.
اما تله بزرگ کجاست؟
یادتونه تو پست "رازهای string" گفتیم که stringها تغییرناپذیر (Immutable) هستن؟ این یعنی هر بار که از + استفاده میکنید، #C یه رشته کاملاً جدید در حافظه میسازه و قبلیها رو دور میریزه.
اگه این کار رو تو یه حلقه انجام بدید، دارید رسماً حافظه رو به آتیش میکشید و برنامهتون رو به شدت کند میکنید!
برای ساختن رشتههای داینامیک در حلقهها، همیشه از کلاس System.Text.StringBuilder استفاده کنید که بعداً مفصل در موردش صحبت میکنیم.
💲 روش مدرن و حرفهای: درونیابی با $ (String Interpolation)
این روش که با گذاشتن $ قبل از رشته فعال میشه، خواناترین و قدرتمندترین راه برای ساختن رشتههاست. شما میتونید متغیرها و عبارات #C رو مستقیم داخل رشته و بین آکولاد {} بنویسید.
فرمتدهی 🎨: میتونید خروجی رو همونجا با گذاشتن : و یه "فرمت استرینگ" کنترل کنید. مثلاً برای نمایش عدد در مبنای هگزادسیمال:
عبارات پیچیده 🤯: اگه عبارتتون پیچیدهست
یا خودش : داره (مثل اپراتور سهتایی)، کل عبارت رو داخل پرانتز بذارید:
رشتههای ثابت (از C# 10) 💎: اگه تمام مقادیر داخل $ ثابت باشن، خود رشته هم میتونه const باشه:
رشتههای چند خطی (از C# 11) 📄: رشتههای درونیابی میتونن چند خطی هم باشن که کار رو خیلی راحتتر میکنه:
🤔 حرف حساب و انتخاب شما
برای چسبوندن دو یا سه تا رشته ساده ⟵ + کار راه اندازه.
برای هر حالت دیگهای، به خصوص وقتی متغیرها رو داخل رشته میارید ⟵ $ (درونیابی) انتخاب اول و آخر شماست. خوانا، قدرتمند و مدرن.
شما تو کدهاتون بیشتر از کدوم روش استفاده میکنید؟ آیا تا حالا با مشکل پرفورمنس به خاطر استفاده زیاد از + تو حلقهها برخورد کردید؟ یا چه ترفند باحالی برای فرمت کردن رشتهها با $ بلدید؟
+ در برابر $ (الحاق یا درونیابی؟)
همهی ما یه زمانی اینجوری رشته میساختیم:
string s = "User: " + name + " | Age: " + age;
این کد کار میکنه، ولی آیا بهترین، خواناترین و بهینهترین راهه؟
بیاید دو روش اصلی ساختن رشته در سیشارپ رو با تمام جزئیاتشون کالبدشکافی کنیم و ببینیم کی باید از کدوم استفاده کرد.
🔗 روش سنتی: الحاق با + و تلهی پرفورمنس
این سادهترین روشه. اپراتور + دو تا رشته رو به هم میچسبونه. اگه یکی از مقادیر رشته نباشه، #C به صورت خودکار متد ()ToString رو روش صدا میزنه.
string s = "User ID: " + 5; // خروجی: "User ID: 5"
اما تله بزرگ کجاست؟
یادتونه تو پست "رازهای string" گفتیم که stringها تغییرناپذیر (Immutable) هستن؟ این یعنی هر بار که از + استفاده میکنید، #C یه رشته کاملاً جدید در حافظه میسازه و قبلیها رو دور میریزه.
اگه این کار رو تو یه حلقه انجام بدید، دارید رسماً حافظه رو به آتیش میکشید و برنامهتون رو به شدت کند میکنید!
راه حل حرفهای (برای موارد پیچیده)
برای ساختن رشتههای داینامیک در حلقهها، همیشه از کلاس System.Text.StringBuilder استفاده کنید که بعداً مفصل در موردش صحبت میکنیم.
💲 روش مدرن و حرفهای: درونیابی با $ (String Interpolation)
این روش که با گذاشتن $ قبل از رشته فعال میشه، خواناترین و قدرتمندترین راه برای ساختن رشتههاست. شما میتونید متغیرها و عبارات #C رو مستقیم داخل رشته و بین آکولاد {} بنویسید.
int x = 4;
Console.WriteLine($"A square has {x} sides."); // خروجی: A square has 4 sides
قدرتهای پنهان درونیابی:
فرمتدهی 🎨: میتونید خروجی رو همونجا با گذاشتن : و یه "فرمت استرینگ" کنترل کنید. مثلاً برای نمایش عدد در مبنای هگزادسیمال:
string s = $"255 in hex is {byte.MaxValue:X2}"; // X2 یعنی هگزادسیمال با ۲ رقم
// خروجی: "255 in hex is FF"عبارات پیچیده 🤯: اگه عبارتتون پیچیدهست
یا خودش : داره (مثل اپراتور سهتایی)، کل عبارت رو داخل پرانتز بذارید:
bool b = true;
Console.WriteLine($"The answer is {(b ? 1 : 0)}");
رشتههای ثابت (از C# 10) 💎: اگه تمام مقادیر داخل $ ثابت باشن، خود رشته هم میتونه const باشه:
const string greeting = "Hello";
const string message = $"{greeting}, world";
رشتههای چند خطی (از C# 11) 📄: رشتههای درونیابی میتونن چند خطی هم باشن که کار رو خیلی راحتتر میکنه:
string s = $"This interpolation spans {1 + 1} lines";🤔 حرف حساب و انتخاب شما
برای چسبوندن دو یا سه تا رشته ساده ⟵ + کار راه اندازه.
برای هر حالت دیگهای، به خصوص وقتی متغیرها رو داخل رشته میارید ⟵ $ (درونیابی) انتخاب اول و آخر شماست. خوانا، قدرتمند و مدرن.
شما تو کدهاتون بیشتر از کدوم روش استفاده میکنید؟ آیا تا حالا با مشکل پرفورمنس به خاطر استفاده زیاد از + تو حلقهها برخورد کردید؟ یا چه ترفند باحالی برای فرمت کردن رشتهها با $ بلدید؟
🔖 هشتگها :
#CSharp
#String
#DotNet
#Performance
⛓️ راهنمای جامع آرایهها (Arrays) در #C : از ساختار تا حافظه
آرایهها یکی از پایهایترین و در عین حال قدرتمندترین ساختارهای داده تو هر زبان برنامهنویسیه. درک عمیقشون برای نوشتن کدهای بهینه و کارآمد، حیاتیه. بیاید یه شیرجه عمیق بزنیم تو دنیای آرایهها در #C و تمام جزئیاتش رو یاد بگیریم.
1️⃣شالوده آرایه: تعریف، ساختار و دسترسی
آرایه، مجموعهای با تعداد ثابت از متغیرهاست که همگی از یک نوع مشخص هستن.
عناصر یک آرایه، همیشه در یک بلوک حافظه پشت سر هم و یکپارچه (Contiguous) قرار میگیرن که باعث دسترسی فوقالعاده سریع به هر کدوم از عناصر میشه.
آرایه رو با [] بعد از نوع داده مشخص میکنیم. برای دسترسی به هر عنصر (element) هم از ایندکس اون استفاده میکنیم که از صفر شروع میشه.
2️⃣مقداردهی و پیمایش: از روش کلاسیک تا C# 12
برای کار با تمام عناصر یک آرایه، معمولاً از حلقهها استفاده میکنیم. پراپرتی .Length هم به ما تعداد کل عناصر آرایه رو میده.
بعد از ساختن یک آرایه، طول (ظرفیت) آن دیگر قابل تغییر نیست. برای ساختارهای داده با اندازه داینامیک، باید از کالکشنهای پیشرفتهتری که در System.Collection وجود داره استفاده کرد.
میتونید موقع تعریف، مستقیماً آرایه رو مقداردهی کنید:
روش مدرن (از C# 12):
از C# 12 به بعد، میتونید از [] به جای {} برای مقداردهی اولیه استفاده کنید. مزیت بزرگش اینه که میتونید مستقیماً اون رو به متدها پاس بدید:
3️⃣راز مقداردهی پیشفرض (Default Initialization)
وقتی یک آرایه رو میسازید، عناصرش هیچوقت خالی نیستن! #C همیشه اونها رو با مقدار پیشفرضِ نوع دادهشون پر میکنه. این کار با صفر کردن بیتهای حافظه (bitwise zeroing) انجام میشه.
برای انواع عددی مثل int، مقدار پیشفرض 0 است.
برای bool، مقدار پیشفرض false است.
برای تمام Reference Typeها (مثل string یا کلاسها)، مقدار پیشفرض null است.
4️⃣مهمترین بحث: آرایهی Value Type در برابر Reference Type
یادتونه در مورد تفاوت Value و Reference Type حرف زدیم؟ اینجا خودشو به بهترین شکل نشون میده!
آرایهای از Value Type (مثل struct):
وقتی آرایهای از structها میسازید، خودِ آبجکتها به صورت فیزیکی و پشت سر هم در اون بلوک حافظه قرار میگیرن.
آرایهای از Reference Type (مثل class):
اما وقتی آرایهای از classها میسازید، اون بلوک حافظه فقط با ۱۰۰۰ تا رفرنس null پر میشه! هنوز هیچ آبجکتی ساخته نشده.
راه حل: باید بعد از ساختن آرایه، در یک حلقه، هر عنصر رو به صورت جداگانه با new بسازید:
5️⃣نکات تکمیلی و کلیدی
نکته 1: خودِ آرایه، همیشه یک Reference Type است، حتی اگه عناصر داخلش Value Type باشن. این یعنی میتونید یه رفرنس آرایه داشته باشید که null باشه:
نکته 2: تمام آرایهها در #C به صورت پنهان از کلاس System.Array ارثبری میکنند که یک سری متدهای عمومی و کاربردی رو در اختیارشون میذاره.
آرایهها یکی از پایهایترین و در عین حال قدرتمندترین ساختارهای داده تو هر زبان برنامهنویسیه. درک عمیقشون برای نوشتن کدهای بهینه و کارآمد، حیاتیه. بیاید یه شیرجه عمیق بزنیم تو دنیای آرایهها در #C و تمام جزئیاتش رو یاد بگیریم.
1️⃣شالوده آرایه: تعریف، ساختار و دسترسی
آرایه، مجموعهای با تعداد ثابت از متغیرهاست که همگی از یک نوع مشخص هستن.
نکته کلیدی پرفورمنس:
عناصر یک آرایه، همیشه در یک بلوک حافظه پشت سر هم و یکپارچه (Contiguous) قرار میگیرن که باعث دسترسی فوقالعاده سریع به هر کدوم از عناصر میشه.
نحوه تعریف و دسترسی:
آرایه رو با [] بعد از نوع داده مشخص میکنیم. برای دسترسی به هر عنصر (element) هم از ایندکس اون استفاده میکنیم که از صفر شروع میشه.
// یک آرایه از نوع char با ظرفیت ۵ عنصر تعریف میکنیم
char[] vowels = new char[5];
// به هر عنصر با ایندکسش دسترسی پیدا میکنیم
vowels[0] = 'a';
vowels[1] = 'e';
vowels[2] = 'i';
vowels[3] = 'o';
vowels[4] = 'u';
Console.WriteLine(vowels[1]); // خروجی: e
2️⃣مقداردهی و پیمایش: از روش کلاسیک تا C# 12
برای کار با تمام عناصر یک آرایه، معمولاً از حلقهها استفاده میکنیم. پراپرتی .Length هم به ما تعداد کل عناصر آرایه رو میده.
for (int i = 0; i < vowels.Length; i++)
{
Console.Write(vowels[i]); // خروجی: aeiou
}
نکته:
بعد از ساختن یک آرایه، طول (ظرفیت) آن دیگر قابل تغییر نیست. برای ساختارهای داده با اندازه داینامیک، باید از کالکشنهای پیشرفتهتری که در System.Collection وجود داره استفاده کرد.
مقداردهی اولیه سریع:
میتونید موقع تعریف، مستقیماً آرایه رو مقداردهی کنید:
// روش قدیمیتر
char[] vowels = new char[] {'a','e','i','o','u'};
// روش سادهتر
char[] vowels = {'a','e','i','o','u'};
روش مدرن (از C# 12):
Collection Expressions
از C# 12 به بعد، میتونید از [] به جای {} برای مقداردهی اولیه استفاده کنید. مزیت بزرگش اینه که میتونید مستقیماً اون رو به متدها پاس بدید:
// تعریف آرایه با سینتکس جدید
char[] vowels = ['a','e','i','o','u'];
// پاس دادن مستقیم به یک متد
PrintLetters(['x', 'y', 'z']);
3️⃣راز مقداردهی پیشفرض (Default Initialization)
وقتی یک آرایه رو میسازید، عناصرش هیچوقت خالی نیستن! #C همیشه اونها رو با مقدار پیشفرضِ نوع دادهشون پر میکنه. این کار با صفر کردن بیتهای حافظه (bitwise zeroing) انجام میشه.
برای انواع عددی مثل int، مقدار پیشفرض 0 است.
برای bool، مقدار پیشفرض false است.
برای تمام Reference Typeها (مثل string یا کلاسها)، مقدار پیشفرض null است.
int[] numbers = new int[1000];
Console.WriteLine(numbers[123]); // خروجی: 0
4️⃣مهمترین بحث: آرایهی Value Type در برابر Reference Type
یادتونه در مورد تفاوت Value و Reference Type حرف زدیم؟ اینجا خودشو به بهترین شکل نشون میده!
آرایهای از Value Type (مثل struct):
وقتی آرایهای از structها میسازید، خودِ آبجکتها به صورت فیزیکی و پشت سر هم در اون بلوک حافظه قرار میگیرن.
public struct Point { public int X, Y; }
Point[] points = new Point[1000];
// اینجا ما ۱۰۰۰ آبجکت Point کامل در حافظه داریم
// و مقدار پیشفرض هر X و Y در آنها، صفر است
Console.WriteLine(points[500].X); // خروجی: 0 آرایهای از Reference Type (مثل class):
اما وقتی آرایهای از classها میسازید، اون بلوک حافظه فقط با ۱۰۰۰ تا رفرنس null پر میشه! هنوز هیچ آبجکتی ساخته نشده.
public class Point { public int X, Y; }
Point[] points = new Point[1000];
// اینجا ما فقط ۱۰۰۰ تا جای خالی (null) داریم
// این کد خطای NullReferenceException میده!
// چون points[500] نال است و X ندارد.
// int x = points[500].X; // ❌ BOOM!راه حل: باید بعد از ساختن آرایه، در یک حلقه، هر عنصر رو به صورت جداگانه با new بسازید:
for (int i = 0; i < points.Length; i++)
{
points[i] = new Point(); // ساختن هر آبجکت به صورت مجزا
}
5️⃣نکات تکمیلی و کلیدی
نکته 1: خودِ آرایه، همیشه یک Reference Type است، حتی اگه عناصر داخلش Value Type باشن. این یعنی میتونید یه رفرنس آرایه داشته باشید که null باشه:
int[] myArray = null;
نکته 2: تمام آرایهها در #C به صورت پنهان از کلاس System.Array ارثبری میکنند که یک سری متدهای عمومی و کاربردی رو در اختیارشون میذاره.
🤔 حرف حساب و تجربه شما
تسلط بر این رفتارها، شما رو از خیلی از خطاهای رایج مثل NullReferenceException نجات میده و بهتون دید عمیقی نسبت به مدیریت حافظه میده.
مهمترین نکتهای که امروز در مورد آرایهها یاد گرفتید چی بود؟ آیا تفاوت رفتار آرایهی struct و class یا اینکه خود آرایه یک Reference Type هست، براتون جدید بود؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[پاتوق گیک های #C]
تسلط بر این رفتارها، شما رو از خیلی از خطاهای رایج مثل NullReferenceException نجات میده و بهتون دید عمیقی نسبت به مدیریت حافظه میده.
مهمترین نکتهای که امروز در مورد آرایهها یاد گرفتید چی بود؟ آیا تفاوت رفتار آرایهی struct و class یا اینکه خود آرایه یک Reference Type هست، براتون جدید بود؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[پاتوق گیک های #C]
🔖 هشتگها :
#CSharp
#Array
🏗 آرایههای چندبعدی:
گاهی وقتا یه لیست ساده یا آرایه یکبعدی جواب کار ما رو نمیده. وقتی با دادههای جدولی، ماتریسها، یا هر ساختار شبکهای سر و کار داریم، باید بریم سراغ آرایههای چندبعدی.
فکر کنید این نوع آرایه مثل یه جدول اکسل یا یه صفحه شطرنجه. یه شبکه کاملاً منظم که تعداد سطر و ستونش از اول مشخص و ثابته. تمام عناصر در یک بلوک حافظه یکپارچه قرار میگیرن.
نحوه تعریف: ابعاد مختلف رو با کاما (,) از هم جدا میکنیم.
پیمایش: برای گرفتن طول هر بعد، از متد GetLength(dimension) استفاده میکنیم.
2️⃣آرایههای دندانهدار ([][]):
آرایهای از آرایهها ⛓️
این یکی فرق داره. به جای یه شبکه منظم، شما یه آرایه دارید که هر خونهش، خودش میتونه یه آرایه دیگه باشه! مثل یه قفسه کتابه که هر طبقهش میتونه تعداد متفاوتی کتاب جا بده.
نحوه تعریف: برای هر بعد، از یک جفت [] جداگانه استفاده میکنیم.
انعطافپذیری: هر آرایه داخلی میتونه طول متفاوتی داشته باشه.
تله و نکته حیاتی: ⚠️
وقتی یه آرایه دندانهدار میسازید، فقط آرایه بیرونی ساخته میشه و تمام خونههای اون null هستن! شما باید خودتون هر آرایه داخلی رو به صورت جداگانه با new بسازید!
🤔 حرف حساب و انتخاب شما
برای دادههای جدولی و ثابت (مثل تصویر یا ماتریس) ⟵ آرایه مستطیلی ([,])
برای دادههایی که هر ردیفشون طول متفاوتی داره ⟵ آرایه دندانهدار ([][])
شما تو پروژههاتون بیشتر با کدوم نوع آرایه چندبعدی سر و کار داشتید؟ یا سناریوی جالبی دارید که توش یکی از این دو نوع به وضوح برتری داشته؟
نظراتتون رو کامنت کنید! 👇
[@CSharpGeeksChat]
مستطیلی ([,]) در برابر دندانهدار ([][])
گاهی وقتا یه لیست ساده یا آرایه یکبعدی جواب کار ما رو نمیده. وقتی با دادههای جدولی، ماتریسها، یا هر ساختار شبکهای سر و کار داریم، باید بریم سراغ آرایههای چندبعدی.
تو #C دو نوع اصلی داریم:1️⃣آرایههای مستطیلی ([,]): یک شبکه منظم ▦
مستطیلی (Rectangular) و دندانهدار (Jagged). بیاید ببینیم فرقشون چیه و کی باید از کدوم استفاده کرد.
فکر کنید این نوع آرایه مثل یه جدول اکسل یا یه صفحه شطرنجه. یه شبکه کاملاً منظم که تعداد سطر و ستونش از اول مشخص و ثابته. تمام عناصر در یک بلوک حافظه یکپارچه قرار میگیرن.
نحوه تعریف: ابعاد مختلف رو با کاما (,) از هم جدا میکنیم.
پیمایش: برای گرفتن طول هر بعد، از متد GetLength(dimension) استفاده میکنیم.
// تعریف یک ماتریس ۳ در ۳
int[,] matrix = new int[3, 3];
// پیمایش و مقداردهی
for (int i = 0; i < matrix.GetLength(0); i++) // طول بعد اول (سطرها)
{
for (int j = 0; j < matrix.GetLength(1); j++) // طول بعد دوم (ستونها)
{
matrix[i, j] = i * 3 + j;
}
}
// مقداردهی اولیه سریع
int[,] matrix2 =
{
{0, 1, 2},
{3, 4, 5},
{6, 7, 8}
};
2️⃣آرایههای دندانهدار ([][]):
آرایهای از آرایهها ⛓️
این یکی فرق داره. به جای یه شبکه منظم، شما یه آرایه دارید که هر خونهش، خودش میتونه یه آرایه دیگه باشه! مثل یه قفسه کتابه که هر طبقهش میتونه تعداد متفاوتی کتاب جا بده.
نحوه تعریف: برای هر بعد، از یک جفت [] جداگانه استفاده میکنیم.
انعطافپذیری: هر آرایه داخلی میتونه طول متفاوتی داشته باشه.
تله و نکته حیاتی: ⚠️
وقتی یه آرایه دندانهدار میسازید، فقط آرایه بیرونی ساخته میشه و تمام خونههای اون null هستن! شما باید خودتون هر آرایه داخلی رو به صورت جداگانه با new بسازید!
// تعریف یک آرایه بیرونی با ظرفیت ۳ (که هر ۳ خانهاش null است)
int[][] jaggedMatrix = new int[3][];
// حالا باید هر آرایه داخلی را جداگانه بسازیم
for (int i = 0; i < jaggedMatrix.Length; i++)
{
jaggedMatrix[i] = new int[i + 3]; // هر ردیف طولی متفاوت دارد
for (int j = 0; j < jaggedMatrix[i].Length; j++)
{
jaggedMatrix[i][j] = i + j;
}
}
🤔 حرف حساب و انتخاب شما
برای دادههای جدولی و ثابت (مثل تصویر یا ماتریس) ⟵ آرایه مستطیلی ([,])
برای دادههایی که هر ردیفشون طول متفاوتی داره ⟵ آرایه دندانهدار ([][])
شما تو پروژههاتون بیشتر با کدوم نوع آرایه چندبعدی سر و کار داشتید؟ یا سناریوی جالبی دارید که توش یکی از این دو نوع به وضوح برتری داشته؟
نظراتتون رو کامنت کنید! 👇
[@CSharpGeeksChat]
🔖 هشتگها :
#CSharp
#Array
✨ خلاصهنویسی آرایهها در #C :
کدنویسی فقط کار کردن کد نیست؛ زیبا، خوانا و مدرن بودنش هم مهمه. تو #C راههای زیادی برای خلاصهنویسی و تمیزتر کردن کد وجود داره.
امروز میخوایم چند تا از بهترین تکنیکها برای تعریف و مقداردهی آرایهها رو یاد بگیریم که کد شما رو یه لول بالاتر میبره.
1️⃣روش اول:
وقتی میخواید یه آرایه رو همزمان با تعریفش مقداردهی کنید، نیازی نیست همیشه از new و نوع داده کامل استفاده کنید. میتونید اونها رو حذف کنید تا کدتون خلاصهتر بشه:
2️⃣ورود var:
کلمه کلیدی var به کامپایلر میگه: "من نوع این متغیر رو نمینویسم، خودت از روی مقداری که بهش میدم، نوعش رو تشخیص بده!".
این قابلیت کد رو خیلی کوتاهتر میکنه و برخلاف جاوااسکریپت، متغیر شما هنوز هم Strongly-Typed هست؛ یعنی نوعش در زمان کامپایل مشخص و ثابت میشه.
3️⃣ترکیب طلایی:
حالا میتونیم این دو قابلیت رو با هم ترکیب کنیم تا به مدرنترین و تمیزترین روش برای تعریف آرایه برسیم.
وقتی از var استفاده میکنید، باید کلمه new رو بیارید، ولی دیگه لازم نیست نوع آرایه رو مشخص کنید. کامپایلر خودش از روی عناصر داخل آکولاد، نوع آرایه رو تشخیص میده.
نکته حرفهای: اگه عناصر آرایه از چند نوع عددی مختلف باشن، کامپایلر سعی میکنه "بهترین نوعی" که همه عناصر بهش قابل تبدیل هستن رو پیدا کنه:
استفاده از var و این تکنیکهای خلاصهنویسی، کد شما رو مدرنتر و خواناتر میکنه، هرچند بعضی برنامهنویسها هنوز ترجیح میدن نوع رو به صورت صریح بنویسن تا خوانایی در نگاه اول بیشتر باشه.
شما تو کدهاتون چقدر از var استفاده میکنید؟ آیا طرفدارش هستید یا ترجیح میدید همیشه نوع متغیر رو به صورت صریح مشخص کنید؟ دلیلش چیه؟
نظراتتون رو کامنت کنید! 👇
[@CSharpGeeksChat]
تکنیکهای var و مقداردهی اولیه
کدنویسی فقط کار کردن کد نیست؛ زیبا، خوانا و مدرن بودنش هم مهمه. تو #C راههای زیادی برای خلاصهنویسی و تمیزتر کردن کد وجود داره.
امروز میخوایم چند تا از بهترین تکنیکها برای تعریف و مقداردهی آرایهها رو یاد بگیریم که کد شما رو یه لول بالاتر میبره.
1️⃣روش اول:
حذف اضافات در مقداردهی اولیه ✂️
وقتی میخواید یه آرایه رو همزمان با تعریفش مقداردهی کنید، نیازی نیست همیشه از new و نوع داده کامل استفاده کنید. میتونید اونها رو حذف کنید تا کدتون خلاصهتر بشه:
// به جای: new char[] {'a','e','i','o','u'}
char[] vowels = {'a','e','i','o','u'};
// این روش برای آرایههای چندبعدی هم کار میکنه
int[,] rectangularMatrix =
{
{0,1,2},
{3,4,5},
{6,7,8}
};2️⃣ورود var:
قدرت استنتاج نوع (Type Inference) 🤖
کلمه کلیدی var به کامپایلر میگه: "من نوع این متغیر رو نمینویسم، خودت از روی مقداری که بهش میدم، نوعش رو تشخیص بده!".
این قابلیت کد رو خیلی کوتاهتر میکنه و برخلاف جاوااسکریپت، متغیر شما هنوز هم Strongly-Typed هست؛ یعنی نوعش در زمان کامپایل مشخص و ثابت میشه.
// کامپایلر میفهمه که i از نوع int هست
var i = 10;
// کامپایلر میفهمه که name از نوع string هست
var name = "#C Geeks";
3️⃣ترکیب طلایی:
var + مقداردهی اولیه آرایه 🏆
حالا میتونیم این دو قابلیت رو با هم ترکیب کنیم تا به مدرنترین و تمیزترین روش برای تعریف آرایه برسیم.
وقتی از var استفاده میکنید، باید کلمه new رو بیارید، ولی دیگه لازم نیست نوع آرایه رو مشخص کنید. کامپایلر خودش از روی عناصر داخل آکولاد، نوع آرایه رو تشخیص میده.
// کامپایلر از روی عناصر، نوع char[] رو استنتاج میکنه
var vowels = new[] {'a','e','i','o','u'};
// برای آرایههای چندبعدی هم به همین شکل
var matrix = new[,]
{
{0,1,2},
{3,4,5}
};
نکته حرفهای: اگه عناصر آرایه از چند نوع عددی مختلف باشن، کامپایلر سعی میکنه "بهترین نوعی" که همه عناصر بهش قابل تبدیل هستن رو پیدا کنه:
// اینجا ۱ یک int و دومی یک long است.
// کامپایلر نوع long[] رو برای کل آرایه انتخاب میکنه
var numbers = new[] {1, 10_000_000_000L};
🤔 حرف حساب و استایل شما
استفاده از var و این تکنیکهای خلاصهنویسی، کد شما رو مدرنتر و خواناتر میکنه، هرچند بعضی برنامهنویسها هنوز ترجیح میدن نوع رو به صورت صریح بنویسن تا خوانایی در نگاه اول بیشتر باشه.
شما تو کدهاتون چقدر از var استفاده میکنید؟ آیا طرفدارش هستید یا ترجیح میدید همیشه نوع متغیر رو به صورت صریح مشخص کنید؟ دلیلش چیه؟
نظراتتون رو کامنت کنید! 👇
[@CSharpGeeksChat]
🔖 هشتگها :
#CSharp
#Var
#CodingTips
#CleanCode
🚧 خطای IndexOutOfRangeException :
چرا آرایهها از لبه پرت میشوند؟
یکی از اولین و رایجترین خطاهای زمان اجرایی که هر برنامهنویس #C باهاش روبرو میشه، همین IndexOutOfRangeException هست. اون لحظهای که فکر میکنی همه چی درسته، ولی برنامه با این خطا کرش میکنه.
اما این خطا دشمن شما نیست، بلکه یه نگهبان مهمه! بیاید ببینیم داستانش چیه و چرا وجودش برای ما خوبه.
وقتی شما یک آرایه میسازید، در واقع یک محدوده مشخص در حافظه تعریف میکنید. Bounds Checking یعنی CLR (محیط اجرای داتنت)، قبل از دسترسی به هر خانهی آرایه، مثل یه نگهبان با دقت چک میکنه که آیا ایندکسی که دادی، تو محدوده مجاز آرایه هست یا نه.
اگه از محدوده خارج بزنید، اون نگهبان جلوتون رو میگیره و خطای IndexOutOfRangeException رو پرتاب میکنه.
شاید اولش از این خطا بدتون بیاد، ولی وجودش دو تا دلیل خیلی مهم داره:
این بررسی جلوی دسترسی و تخریب ناخواسته بخشهای دیگه حافظه رو میگیره. اگه این خطا نبود، ممکن بود برنامهتون دادههای دیگه رو خراب کنه و هیچوقت نفهمید مشکل از کجاست.
به جای اینکه برنامهتون با یه رفتار عجیب و غریب به کارش ادامه بده و بعداً یه جای نامربوط کرش کنه، دقیقاً همون لحظه و همون خطی که اشتباه کردی، بهت خطا میده. این دیباگ کردن رو فوقالعاده راحتتر میکنه.
شاید فکر کنید این همه چک کردن، برنامه رو کند میکنه. درسته که یه هزینه خیلی جزئی داره، ولی کامپایلر JIT (Just-In-Time) خیلی هوشمنده.
مثلاً، قبل از ورود به یه حلقه، اگه بفهمه تمام ایندکسها امن هستن، چک کردن رو برای کل حلقه بهینه میکنه. پس در ۹۹٪ مواقع، تأثیر این بررسی روی پرفورمنس ناچیز و قابل چشمپوشی است.
پس دفعه بعدی که این خطا رو دیدید، بهش به چشم یه دشمن نگاه نکنید، بلکه ازش به عنوان یه راهنما که داره اشتباهتون رو دقیق نشون میده، تشکر کنید!
اولین باری که به این خطا برخوردید یادتونه؟ داشتید روی چه کدی کار میکردید؟ بیاید از خاطرات مشترک برنامهنویسیمون بگیم!
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[@CSharpGeeksChat]
چرا آرایهها از لبه پرت میشوند؟
یکی از اولین و رایجترین خطاهای زمان اجرایی که هر برنامهنویس #C باهاش روبرو میشه، همین IndexOutOfRangeException هست. اون لحظهای که فکر میکنی همه چی درسته، ولی برنامه با این خطا کرش میکنه.
اما این خطا دشمن شما نیست، بلکه یه نگهبان مهمه! بیاید ببینیم داستانش چیه و چرا وجودش برای ما خوبه.
1️⃣ بررسی محدوده (Bounds Checking) چیست؟
وقتی شما یک آرایه میسازید، در واقع یک محدوده مشخص در حافظه تعریف میکنید. Bounds Checking یعنی CLR (محیط اجرای داتنت)، قبل از دسترسی به هر خانهی آرایه، مثل یه نگهبان با دقت چک میکنه که آیا ایندکسی که دادی، تو محدوده مجاز آرایه هست یا نه.
اگه از محدوده خارج بزنید، اون نگهبان جلوتون رو میگیره و خطای IndexOutOfRangeException رو پرتاب میکنه.
// آرایهای با ۳ عنصر که ایندکسهای مجاز آن 0, 1, 2 هستند
int[] arr = new int[3];
arr[0] = 10; // ✅ مجاز
arr[2] = 30; // ✅ مجاز
// تلاش برای دسترسی به ایندکس 3 که وجود نداره!
arr[3] = 40; // 💥 BOOM! IndexOutOfRangeException
2️⃣چرا این خطا یک "ویژگی" خوب است؟ ✅
شاید اولش از این خطا بدتون بیاد، ولی وجودش دو تا دلیل خیلی مهم داره:
امنیت و پایداری (Safety) :
این بررسی جلوی دسترسی و تخریب ناخواسته بخشهای دیگه حافظه رو میگیره. اگه این خطا نبود، ممکن بود برنامهتون دادههای دیگه رو خراب کنه و هیچوقت نفهمید مشکل از کجاست.
دیباگ آسان :
به جای اینکه برنامهتون با یه رفتار عجیب و غریب به کارش ادامه بده و بعداً یه جای نامربوط کرش کنه، دقیقاً همون لحظه و همون خطی که اشتباه کردی، بهت خطا میده. این دیباگ کردن رو فوقالعاده راحتتر میکنه.
3️⃣آیا این بررسی روی پرفورمنس تأثیر دارد؟ 🚀
شاید فکر کنید این همه چک کردن، برنامه رو کند میکنه. درسته که یه هزینه خیلی جزئی داره، ولی کامپایلر JIT (Just-In-Time) خیلی هوشمنده.
مثلاً، قبل از ورود به یه حلقه، اگه بفهمه تمام ایندکسها امن هستن، چک کردن رو برای کل حلقه بهینه میکنه. پس در ۹۹٪ مواقع، تأثیر این بررسی روی پرفورمنس ناچیز و قابل چشمپوشی است.
نکته حرفهای: البته #C به شما اجازه میده در شرایط خاص و با مسئولیت خودتان، با استفاده از کد unsafe این بررسی امنیتی رو دور بزنید، که یه بحث خیلی پیشرفتهست.🤔 حرف حساب و خاطرات شما
پس دفعه بعدی که این خطا رو دیدید، بهش به چشم یه دشمن نگاه نکنید، بلکه ازش به عنوان یه راهنما که داره اشتباهتون رو دقیق نشون میده، تشکر کنید!
اولین باری که به این خطا برخوردید یادتونه؟ داشتید روی چه کدی کار میکردید؟ بیاید از خاطرات مشترک برنامهنویسیمون بگیم!
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[@CSharpGeeksChat]
🔖 هشتگها :
#CSharp
#ErrorHandling
#DotNet
#Exception
🧠 کالبدشکافی حافظه در C# Stack در برابر Heap
وقتی یه متغیر تعریف میکنید، دقیقاً کجا میره؟ چرا بعضی متغیرها بلافاصله بعد از خروج از متد از بین میرن ولی بعضی دیگه باقی میمونن؟
جواب این سوالها تو دو تا از مهمترین مفاهیم کامپیوتر نهفتهست: Stack (پشته) و Heap (هیپ). درک این دو، شما رو از یه کدنویس ساده به یه توسعهدهنده واقعی تبدیل میکنه.
1️⃣پشته (Stack) :
حافظهی سریع و منظم فکر کنید Stack مثل یه دسته بشقابه که روی هم چیدید. آخرین بشقابی که میذارید، اولین بشقابیه که برمیدارید (LIFO: Last-In, First-Out).
چی توش ذخیره میشه؟ این بخش از حافظه برای ذخیرهی متغیرهای محلی و پارامترهای متدها استفاده میشه. بیشتر Value Type ها اینجا زندگی میکنن.
چطور کار میکنه؟ با هر بار صدا زدن یک متد، حافظه مورد نیاز اون متد بالای پشته قرار میگیره و با تمام شدن متد، اون بخش از حافظه بلافاصله آزاد میشه. این کار فوقالعاده سریعه.
static int Factorial (int x)
{
if (x == 0) return 1;
return x * Factorial (x - 1);
}
هر بار که Factorial خودشو صدا میزنه، یه int x جدید روی پشته ساخته میشه و با برگشتن جواب، اون int از روی پشته برداشته میشه.
2️⃣هیپ (Heap) :
هیپ مثل یه انبار بزرگه. هر وقت یه آبجکت جدید میسازید (new)، CLR یه جای خالی تو این انبار پیدا میکنه، آبجکت رو اونجا میذاره و یه "آدرس" یا "رفرنس" به شما برمیگردونه.
چی توش ذخیره میشه؟ تمام آبجکتها (نمونههای Reference Type) اینجا زندگی میکنن.
چطور کار میکنه؟ این حافظه توسط یه رفتگر هوشمند به اسم Garbage Collector (GC) مدیریت میشه. GC هر چند وقت یکبار میاد و آبجکتهایی که دیگه هیچ رفرنسی بهشون اشاره نمیکنه (آبجکتهای یتیم) رو از حافظه پاک میکنه تا فضا آزاد بشه.
StringBuilder ref1 = new StringBuilder("obj1");
Console.WriteLine(ref1);
// بعد از این خط، دیگه هیچ رفرنسی به آبجکت "obj1" اشاره نمیکنه
// و آماده پاک شدن توسط GC میشه.
StringBuilder ref2 = new StringBuilder("obj2");
StringBuilder ref3 = ref2;
// اینجا با اینکه ref2 دیگه استفاده نمیشه، چون ref3 هنوز به "obj2"
// اشاره میکنه، این آبجکت زنده میمونه!
Console.WriteLine(ref3);نکته: فیلدهای استاتیک هم روی هیپ ذخیره میشن، ولی تا پایان عمر برنامه زنده میمونن.
🤔 پس Value Typeها دقیقاً کجا زندگی میکنند؟
این سوال مهمیه! قانونش اینه: Value Typeها هرجا که تعریف بشن، همونجا زندگی میکنن.
اگه به عنوان یه متغیر محلی تو یه متد تعریف بشن ⟵ روی Stack.
اگه به عنوان یه فیلد داخل یه کلاس (که خودش یه Reference Type هست) تعریف بشن ⟵ همراه با اون آبجکت روی Heap!
حرف حساب و درک عمیقتر
درک تفاوت Stack و Heap فقط یه بحث تئوری نیست؛ روی پرفورمنس، مدیریت حافظه و حتی نحوه طراحی کلاسهای شما تأثیر مستقیم داره.
این توضیح چقدر به درک بهتر شما از رفتار Value Type و Reference Type کمک کرد؟ آیا نکتهای در مورد Stack و Heap بود که براتون تازگی داشته باشه؟
نظراتتون رو کامنت کنید! 👇
[پاتوق گیک های #C]
🔖 هشتگها :
#CSharp
#MemoryManagement
#DotNet #Stack #Heap
🛡 سیاست تخصیص قطعی در #C: چرا کامپایلر از شما باهوشتر است؟
تا حالا براتون سوال شده چرا گاهی اوقات اگه به یه متغیر مقدار اولیه ندید، کامپایلر ازتون ایراد میگیره، ولی گاهی اوقات خودش بهش مقدار صفر یا null میده؟
این رفتار شانسی نیست! پشتش یه قانون مهم و امنیتی در #C به اسم "سیاست تخصیص قطعی" (Definite Assignment) خوابیده. این قانون میگه شما هرگز نمیتونید به حافظهای که مقداردهی اولیه نشده، دسترسی داشته باشید.
1️⃣قانون برای متغیرهای محلی: خودت باید مقدار بدی!
وقتی یه متغیر داخل یک متد تعریف میکنی (Local Variable)، #C به تو اعتماد میکنه و وظیفه مقداردهی اولیه رو به عهده خودت میذاره. اگر قبل از اینکه مقداری بهش بدی، سعی کنی ازش استفاده کنی (بخونیش)، کامپایلر جلوت رو میگیره و خطای زمان کامپایل (Compile-time error) میده.
2️⃣قانون برای فیلدها و آرایهها: همیشه مقدار دارند! ✨
اما برای اعضایی که طول عمر بیشتری دارن، مثل فیلدهای یک کلاس (چه static و چه instance) یا عناصر یک آرایه، داستان فرق میکنه.
اینجا NET runtime. برای جلوگیری از مشکلات، همیشه اونها رو به صورت خودکار با مقدار پیشفرضِ نوع دادهشون مقداردهی میکنه.
🤔 چرا این تفاوت وجود دارد؟
این یه تصمیم هوشمندانه برای امنیته. برای متغیرهای محلی، کامپایلر میتونه به سادگی و با قاطعیت تشخیص بده که شما فراموش کردید مقداردهی کنید و همونجا جلوتون رو بگیره. اما برای فیلدها و آرایهها که چرخه حیاتشون پیچیدهتره، مقداردهی خودکار توسط runtime، جلوی باگهای خطرناک ناشی از حافظهی مقداردهی نشده رو میگیره.
پس این "ایراد گرفتن" کامپایلر، در واقع یه کمک بزرگه!
نظراتتون رو کامنت کنید! 👇
[C# Geeks Hangout]
تا حالا براتون سوال شده چرا گاهی اوقات اگه به یه متغیر مقدار اولیه ندید، کامپایلر ازتون ایراد میگیره، ولی گاهی اوقات خودش بهش مقدار صفر یا null میده؟
این رفتار شانسی نیست! پشتش یه قانون مهم و امنیتی در #C به اسم "سیاست تخصیص قطعی" (Definite Assignment) خوابیده. این قانون میگه شما هرگز نمیتونید به حافظهای که مقداردهی اولیه نشده، دسترسی داشته باشید.
1️⃣قانون برای متغیرهای محلی: خودت باید مقدار بدی!
وقتی یه متغیر داخل یک متد تعریف میکنی (Local Variable)، #C به تو اعتماد میکنه و وظیفه مقداردهی اولیه رو به عهده خودت میذاره. اگر قبل از اینکه مقداری بهش بدی، سعی کنی ازش استفاده کنی (بخونیش)، کامپایلر جلوت رو میگیره و خطای زمان کامپایل (Compile-time error) میده.
void MyMethod()
{
int x;
// ❌ خطای زمان کامپایل!
// کامپایلر میگه: "تو به من نگفتی تو x چی بریزم!"
Console.WriteLine(x);
}
2️⃣قانون برای فیلدها و آرایهها: همیشه مقدار دارند! ✨
اما برای اعضایی که طول عمر بیشتری دارن، مثل فیلدهای یک کلاس (چه static و چه instance) یا عناصر یک آرایه، داستان فرق میکنه.
اینجا NET runtime. برای جلوگیری از مشکلات، همیشه اونها رو به صورت خودکار با مقدار پیشفرضِ نوع دادهشون مقداردهی میکنه.
// آرایهها همیشه با مقادیر پیشفرض پر میشن (برای int، صفره)
int[] numbers = new int[3];
Console.WriteLine(numbers[0]); // خروجی: 0
class Test
{
// فیلدها هم همیشه مقدار پیشفرض میگیرن
public static int X;
}
// ...
Console.WriteLine(Test.X); // خروجی: 0
🤔 چرا این تفاوت وجود دارد؟
این یه تصمیم هوشمندانه برای امنیته. برای متغیرهای محلی، کامپایلر میتونه به سادگی و با قاطعیت تشخیص بده که شما فراموش کردید مقداردهی کنید و همونجا جلوتون رو بگیره. اما برای فیلدها و آرایهها که چرخه حیاتشون پیچیدهتره، مقداردهی خودکار توسط runtime، جلوی باگهای خطرناک ناشی از حافظهی مقداردهی نشده رو میگیره.
پس این "ایراد گرفتن" کامپایلر، در واقع یه کمک بزرگه!
نظراتتون رو کامنت کنید! 👇
[C# Geeks Hangout]
🔖 هشتگها :
#CSharp
#Compiler
#DotNet #SoftwareEngineering
🐣 مقادیر پیشفرض (Default Values) در #C :
هر متغیر با چه مقداری متولد میشود؟
تو پست "سیاست تخصیص قطعی" دیدیم که فیلدهای یک کلاس و عناصر یک آرایه به صورت خودکار مقداردهی اولیه میشن. اما سوال اینجاست: چه مقداری؟ این مقادیر از کجا میان؟
جواب تو یه مفهوم ساده به اسم مقدار پیشفرض (Default Value) هست.
هر نوع دادهای در #C یک مقدار پیشفرض داره که حاصل صفر کردن بیتهای حافظه (Bitwise Zeroing) هست. این یعنی:
●تمام Reference Typeها (کلاس، string، آرایه، ...): مقدار پیشفرضشون null هست.
●تمام Numeric Typeها (int, double, decimal, ...): مقدار پیشفرضشون 0 هست.
●نوع bool: مقدار پیشفرصش false هست.
●نوع char: مقدار پیشفرصش '\0' (کاراکتر نال) هست.
سیشارپ یه کلمه کلیدی شیک و کاربردی به اسم default در اختیار ما میذاره تا بتونیم مقدار پیشفرض هر نوعی رو به راحتی بدست بیاریم، بدون اینکه نیازی به حفظ کردنشون داشته باشیم.
مقدار پیشفرض برای یک struct سفارشی، خیلی سادهست: معادل مقدار پیشفرض تمام فیلدهای داخل اون struct هست.
دونستن مقادیر پیشفرض به شما کمک میکنه رفتار اولیه کدهاتون رو بهتر درک کنید و از خطاهای ناشی از مقادیر ناخواسته جلوگیری کنید.
آیا تا حالا از کلمه کلیدی default تو کدهاتون استفاده کردید؟ یا جایی بوده که مقداردهی پیشفرض یه فیلد یا آرایه شما رو غافلگیر کرده باشه؟
نظراتتون رو کامنت کنید! 👇
[پاتوق گیک های #C]
هر متغیر با چه مقداری متولد میشود؟
تو پست "سیاست تخصیص قطعی" دیدیم که فیلدهای یک کلاس و عناصر یک آرایه به صورت خودکار مقداردهی اولیه میشن. اما سوال اینجاست: چه مقداری؟ این مقادیر از کجا میان؟
جواب تو یه مفهوم ساده به اسم مقدار پیشفرض (Default Value) هست.
مقادیر پیشفرض چیست؟ 🔢
هر نوع دادهای در #C یک مقدار پیشفرض داره که حاصل صفر کردن بیتهای حافظه (Bitwise Zeroing) هست. این یعنی:
●تمام Reference Typeها (کلاس، string، آرایه، ...): مقدار پیشفرضشون null هست.
●تمام Numeric Typeها (int, double, decimal, ...): مقدار پیشفرضشون 0 هست.
●نوع bool: مقدار پیشفرصش false هست.
●نوع char: مقدار پیشفرصش '\0' (کاراکتر نال) هست.
کلمه کلیدی default ✨
سیشارپ یه کلمه کلیدی شیک و کاربردی به اسم default در اختیار ما میذاره تا بتونیم مقدار پیشفرض هر نوعی رو به راحتی بدست بیاریم، بدون اینکه نیازی به حفظ کردنشون داشته باشیم.
// گرفتن مقدار پیشفرض decimal
decimal d1 = default(decimal); // حاصل: 0
// روش مدرنتر و خلاصهتر
// کامپایلر خودش نوع رو از متغیر تشخیص میده
int i = default; // حاصل: 0
bool b = default; // حاصل: false
string s = default; // حاصل: null
تکلیف structها چیست؟ 🏗
مقدار پیشفرض برای یک struct سفارشی، خیلی سادهست: معادل مقدار پیشفرض تمام فیلدهای داخل اون struct هست.
public struct Point
{
public int X; // پیشفرض: 0
public string Name; // پیشفرض: null
}
Point p = default;
// اینجا p.X برابر 0 و p.Name برابر null خواهد بود
Console.WriteLine($"X: {p.X}, Name: {p.Name ?? "null"}");
🤔 حرف حساب و تجربه شما
دونستن مقادیر پیشفرض به شما کمک میکنه رفتار اولیه کدهاتون رو بهتر درک کنید و از خطاهای ناشی از مقادیر ناخواسته جلوگیری کنید.
آیا تا حالا از کلمه کلیدی default تو کدهاتون استفاده کردید؟ یا جایی بوده که مقداردهی پیشفرض یه فیلد یا آرایه شما رو غافلگیر کرده باشه؟
نظراتتون رو کامنت کنید! 👇
[پاتوق گیک های #C]
🔖 هشتگها :
#CSharp
#CodingTips
#SoftwareEngineering
📮 پاس دادن پارامترها در #C :
راز بزرگ کپی شدن "مقدار" و "رفرنس"
یکی از بنیادیترین مفاهیمی که هر توسعهدهنده #C باید بهش مسلط باشه، نحوه پاس دادن پارامترها به متدهاست.
قانون اصلی اینه: در #C، همه چیز به صورت پیشفرض بر اساس مقدار (Pass-by-Value) پاس داده میشه. اما این جمله ساده، برای Value Type و Reference Type دو معنی کاملاً متفاوت داره!
1️⃣سناریو اول: پاس دادن Value Type (کپی کامل)
وقتی یه Value Type (مثل int, struct, bool) رو به یه متد میدید، انگار یه فتوکپی کامل از مقدار اون متغیر میگیرید و اون کپی رو به متد میدید.
متد با اون کپی هر کاری بکنه، اصل سند شما دست نخورده باقی میمونه.
int x = 8;
// یک کپی از مقدار 8 به متد فرستاده میشه
Foo(x);
// x اینجا هنوز همون 8 باقی مونده!
Console.WriteLine(x); // خروجی: 8
static void Foo(int p)
{
// p فقط یک کپی از x هست و در یک جای جدا در حافظه قرار داره
p = p + 1;
Console.WriteLine(p); // خروجی: 9
}
2️⃣سناریو دوم: پاس دادن Reference Type (کپی کلید!) 🔑
اینجا داستان کاملاً فرق میکنه و همونجاییه که خیلیها به اشتباه میفتن.
وقتی یه Reference Type (مثل یه class یا StringBuilder) رو به متد پاس میدید، شما خودِ آبجکت (خونه) رو بهش نمیدید، بلکه یه کپی از کلید (رفرنس) اون خونه رو بهش میدید.
این یعنی حالا دو تا کلید داریم که هر دو، یک در رو باز میکنن!
این رفتار دو تا نتیجه مهم داره:
اگه محتوای خونه رو عوض کنیم، برای همه عوض میشه:
متد با کلید کپی شده میتونه بره تو همون خونه و دکوراسیون رو عوض کنه. این تغییر روی آبجکت اصلی تأثیر میذاره چون هر دو کلید یه خونه رو باز میکنن.
اگه کلید کپی شده رو عوض کنیم، کلید اصلی ما امنه:
ولی اگه متد، خودِ کلید کپی شده رو با یه کلید دیگه عوض کنه (مثلاً fooSB = null)، شما هنوز کلید اصلی خودتون رو دارید و آبجکت اصلی شما null نمیشه.
StringBuilder sb = new StringBuilder("original");
Foo(sb);
// محتوای sb تغییر کرده چون متد، دکوراسیون خونه رو عوض کرد
Console.WriteLine(sb.ToString()); // خروجی: original-test
static void Foo(StringBuilder fooSB)
{
// ۱. با کلید کپی شده، محتوای آبجکت اصلی رو تغییر میدیم
fooSB.Append("-test");
// ۲. خودِ کلید کپی شده رو null میکنیم.
// این کار هیچ تأثیری روی متغیر sb اصلی نداره.
fooSB = null;
}🤔 حرف حساب و تجربه شما
درک این تفاوت، کلید اصلی جلوگیری از باگهای غیرمنتظرهایه که توش دادهها به شکلی که انتظار ندارید، تغییر میکنن.
آیا این رفتار برای پاس دادن Reference Typeها براتون کاملاً روشن بود؟ یا تا حالا با مشکلی مواجه شدید که توش یه آبجکت داخل یه متد تغییر کرده و شما رو غافلگیر کرده باشه؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[C# Geeks Hangout]
🔖 هشتگها :
#CSharp
#Parameters
#ReferenceTypes #ValueTypes
🔄 ابزار حرفهای #C :
تا حالا شده بخوای یه متغیر رو به یه متد پاس بدی و خودِ متد بتونه مقدار اصلی اون متغیر رو تغییر بده؟ به صورت پیشفرض این کار ممکن نیست (چون در حالت عادی، فقط یک کپی از مقدار پاس داده میشه).
اما #C یه ابزار قدرتمند برای این کار داره:
وقتی از ref استفاده میکنی، دیگه کپی مقدار رو به متد نمیدی، بلکه خودِ آدرس حافظه متغیر رو بهش میدی. حالا متد و کد اصلی، هر دو به یک خانه از حافظه نگاه میکنن و هر تغییری، همه جا اعمال میشه.
مثال کلاسیک و بهترین کاربرد ref (متد Swap):
استفاده از ref برای مواقع خاص و قدرتمندیه و به شما کنترل بیشتری روی حافظه میده.
شما تا حالا کجا از ref استفاده کردید که واقعاً به دردتون خورده باشه؟
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[پاتوق گیک های #C]
تغییر متغیرها از راه دور با ref
تا حالا شده بخوای یه متغیر رو به یه متد پاس بدی و خودِ متد بتونه مقدار اصلی اون متغیر رو تغییر بده؟ به صورت پیشفرض این کار ممکن نیست (چون در حالت عادی، فقط یک کپی از مقدار پاس داده میشه).
اما #C یه ابزار قدرتمند برای این کار داره:
کلمه کلیدی ref.
وقتی از ref استفاده میکنی، دیگه کپی مقدار رو به متد نمیدی، بلکه خودِ آدرس حافظه متغیر رو بهش میدی. حالا متد و کد اصلی، هر دو به یک خانه از حافظه نگاه میکنن و هر تغییری، همه جا اعمال میشه.
قانون مهم: باید از کلمه کلیدی ref هم در تعریف متد و هم موقع صدا زدنش استفاده کنی. این کار باعث میشه کد کاملاً خوانا باشه و همه بفهمن که اینجا قراره متغیر اصلی تغییر کنه.
مثال کلاسیک و بهترین کاربرد ref (متد Swap):
static void Swap(ref string a, ref string b)
{
// چون a و b به متغیرهای اصلی اشاره دارن،
// هر تغییری اینجا، مستقیماً روی x و y تأثیر میذاره.
string temp = a;
a = b;
b = temp;
}
// --- نحوه استفاده ---
string x = "Penn";
string y = "Teller";
// با ref متغیرهای اصلی رو به متد میفرستیم
Swap(ref x, ref y);
Console.WriteLine(x); // خروجی: Teller
Console.WriteLine(y); // خروجی: Penn
🤔 حرف حساب و تجربه شما
استفاده از ref برای مواقع خاص و قدرتمندیه و به شما کنترل بیشتری روی حافظه میده.
شما تا حالا کجا از ref استفاده کردید که واقعاً به دردتون خورده باشه؟
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[پاتوق گیک های #C]
🔖 هشتگها:
#CSharp
#DotNet
#RefKeyword
📤 ابزار حرفهای #C :
تا حالا شده بخوای یه متد بنویسی که بیشتر از یه مقدار رو برگردونه؟ مثلاً هم نتیجه عملیات و هم یه پیام وضعیت؟ تو #C برای این کار یه ابزار فوقالعاده داریم:
این کلمه کلیدی به شما اجازه میده پارامترهایی تعریف کنید که مقدارشون از داخل متد به بیرون منتقل میشه.
کلمه کلیدی out هم مثل ref، پارامتر رو بر اساس رفرنس پاس میده (یعنی آدرس حافظه رو میفرسته). اما دو تا قانون مهم و متفاوت داره:
1️⃣نیازی نیست قبل از متد مقداردهی بشه: شما میتونید یه متغیر تعریف نشده رو به یه پارامتر out پاس بدید.
2️⃣حتماً باید داخل متد مقداردهی بشه: متد موظفه که قبل از تموم شدن، یه مقداری رو تو اون پارامتر بریزه. این قانون توسط کامپایلر چک میشه.
مثال: فرض کنید میخوایم یه متد بنویسیم که اسم کامل رو بگیره و اسم کوچک و فامیل رو جدا کنه.
برای راحتتر شدن کار، #C دو تا قابلیت باحال برای out اضافه کرده:
1️⃣تعریف متغیر در لحظه (Out Variables):
لازم نیست متغیر رو از قبل تعریف کنید! میتونید همونجا موقع صدا زدن متد، تعریفش کنید:
2️⃣نادیده گرفتن خروجی (Discards):
اگه به یکی از خروجیها نیاز ندارید، میتونید با یه آندرلاین (_) اون رو نادیده بگیرید تا کدتون تمیزتر بشه:
این out یکی از پرکاربردترین کلمات کلیدی در کتابخانههای داتنته (مثلاً متد معروف int.TryParse). بلد بودنش خیلی به کارتون میاد.
شما بیشتر کجا از out استفاده کردید؟ آیا از قابلیت تعریف متغیر همزمان یا Discard خبر داشتید؟
نظراتتون رو کامنت کنید! 👇
[C# Geeks Hangout]
بازگرداندن چند مقدار از متد با out
تا حالا شده بخوای یه متد بنویسی که بیشتر از یه مقدار رو برگردونه؟ مثلاً هم نتیجه عملیات و هم یه پیام وضعیت؟ تو #C برای این کار یه ابزار فوقالعاده داریم:
کلمه کلیدی out.
این کلمه کلیدی به شما اجازه میده پارامترهایی تعریف کنید که مقدارشون از داخل متد به بیرون منتقل میشه.
این out چیست و چه فرقی با ref داره؟
کلمه کلیدی out هم مثل ref، پارامتر رو بر اساس رفرنس پاس میده (یعنی آدرس حافظه رو میفرسته). اما دو تا قانون مهم و متفاوت داره:
1️⃣نیازی نیست قبل از متد مقداردهی بشه: شما میتونید یه متغیر تعریف نشده رو به یه پارامتر out پاس بدید.
2️⃣حتماً باید داخل متد مقداردهی بشه: متد موظفه که قبل از تموم شدن، یه مقداری رو تو اون پارامتر بریزه. این قانون توسط کامپایلر چک میشه.
کاربرد اصلی: بازگرداندن چند مقدار
بهترین کاربرد out، زمانیه که میخواید از یک متد، چند مقدار خروجی بگیرید.
مثال: فرض کنید میخوایم یه متد بنویسیم که اسم کامل رو بگیره و اسم کوچک و فامیل رو جدا کنه.
void Split(string fullName, out string firstName, out string lastName)
{
int i = fullName.LastIndexOf(' ');
// متد موظفه هر دو پارامتر out رو مقداردهی کنه
firstName = fullName.Substring(0, i);
lastName = fullName.Substring(i + 1);
}
// --- نحوه استفاده ---
string fName, lName; // لازم نیست مقدار اولیه داشته باشن
Split("Stevie Ray Vaughan", out fName, out lName);
Console.WriteLine(fName); // خروجی: Stevie Ray
Console.WriteLine(lName); // خروجی: Vaughn
ترفندهای مدرن: out variables و Discards (_) ✨
برای راحتتر شدن کار، #C دو تا قابلیت باحال برای out اضافه کرده:
1️⃣تعریف متغیر در لحظه (Out Variables):
لازم نیست متغیر رو از قبل تعریف کنید! میتونید همونجا موقع صدا زدن متد، تعریفش کنید:
Split("Stevie Ray Vaughan", out string firstName, out string lastName);
Console.WriteLine(lastName); // Vaughan2️⃣نادیده گرفتن خروجی (Discards):
اگه به یکی از خروجیها نیاز ندارید، میتونید با یه آندرلاین (_) اون رو نادیده بگیرید تا کدتون تمیزتر بشه:
// ما اینجا فقط به اسم کوچک نیاز داریم
Split("Stevie Ray Vaughan", out string fName, out _);
Console.WriteLine(fName); // Stevie Ray
🤔 حرف حساب و تجربه شما
این out یکی از پرکاربردترین کلمات کلیدی در کتابخانههای داتنته (مثلاً متد معروف int.TryParse). بلد بودنش خیلی به کارتون میاد.
شما بیشتر کجا از out استفاده کردید؟ آیا از قابلیت تعریف متغیر همزمان یا Discard خبر داشتید؟
نظراتتون رو کامنت کنید! 👇
[C# Geeks Hangout]
🔖 هشتگها:
#CSharp
#DotNet
#OutKeyword
🛡 ابزار حرفهای #C :
فرض کنید یه struct خیلی بزرگ دارید (مثلاً با کلی فیلد). وقتی این struct رو به یه متد پاس میدید، #C یه کپی کامل ازش میسازه که میتونه روی پرفورمنس تأثیر منفی بذاره.
حالا اگه نخوایم این کپیکاری پرهزینه انجام بشه، ولی در عین حال مطمئن باشیم که متد، مقدار اصلی رو به هیچ وجه تغییر نمیده، چیکار کنیم؟
حالا in چیست؟ رفرنسِ فقط-خواندنی (Read-Only)
"این پارامتر رو بر اساس رفرنس بفرست تا از کپی کردنش جلوگیری بشه، ولی داخل متد، باهاش مثل یه مقدار فقط-خواندنی (Read-Only) رفتار کن و اجازه هیچ تغییری بهش نده."
🧨پس in دو تا کار خفن رو با هم انجام میده:
فهمیدیم in یکی از اون ابزارهای تخصصیتره که نشون میده شما به پرفورمنس و جزئیات اهمیت میدید. این کلمه کلیدی، قدرت ref (جلوگیری از کپی) و ایمنی pass-by-value (عدم تغییر مقدار اصلی) رو با هم ترکیب میکنه.
شما تا حالا از پارامتر in برای بهینهسازی کدهاتون استفاده کردید؟ یا اصلاً از وجودش خبر داشتید؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[پاتوق گیک های #C]
بهینهسازی پرفورمنس با پارامتر in
فرض کنید یه struct خیلی بزرگ دارید (مثلاً با کلی فیلد). وقتی این struct رو به یه متد پاس میدید، #C یه کپی کامل ازش میسازه که میتونه روی پرفورمنس تأثیر منفی بذاره.
حالا اگه نخوایم این کپیکاری پرهزینه انجام بشه، ولی در عین حال مطمئن باشیم که متد، مقدار اصلی رو به هیچ وجه تغییر نمیده، چیکار کنیم؟
✨️اینجاست که کلمه کلیدی in وارد میدان میشه.
حالا in چیست؟ رفرنسِ فقط-خواندنی (Read-Only)
کلمه کلیدی in به کامپایلر میگه:
"این پارامتر رو بر اساس رفرنس بفرست تا از کپی کردنش جلوگیری بشه، ولی داخل متد، باهاش مثل یه مقدار فقط-خواندنی (Read-Only) رفتار کن و اجازه هیچ تغییری بهش نده."
🧨پس in دو تا کار خفن رو با هم انجام میده:
1️⃣افزایش پرفورمنس: از کپی شدن structهای بزرگ جلوگیری میکنه.
2️⃣تضمین ایمنی: به شما اطمینان میده که متد، دادهی اصلی شما رو دستکاری نمیکنه.
// فرض کنید این یک استراکت خیلی بزرگه
public readonly struct BigStruct
{
// ... کلی فیلد و پراپرتی
}
void ProcessData(in BigStruct data)
{
// این کد کامپایل نمیشه چون پارامتر 'data' فقط-خواندنی است
// data = new BigStruct(); // ❌ Compile-time error!
// ولی میتونیم ازش بخونیم و استفاده کنیم
Console.WriteLine("Processing data...");
}
// --- نحوه استفاده ---
var myBigData = new BigStruct();
// با 'in' به متد پاس میدیم تا از کپی شدن جلوگیری کنیم
ProcessData(in myBigData);
// اگه ابهامی در اورلود متد نباشه، بدون 'in' هم میشه صداش زد
ProcessData(myBigData);
🤔حرف حساب و تجربه شما
فهمیدیم in یکی از اون ابزارهای تخصصیتره که نشون میده شما به پرفورمنس و جزئیات اهمیت میدید. این کلمه کلیدی، قدرت ref (جلوگیری از کپی) و ایمنی pass-by-value (عدم تغییر مقدار اصلی) رو با هم ترکیب میکنه.
شما تا حالا از پارامتر in برای بهینهسازی کدهاتون استفاده کردید؟ یا اصلاً از وجودش خبر داشتید؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[پاتوق گیک های #C]
🔖 هشتگها:
#CSharp
#Performance
#CodingTips
✨ متدهای انعطافپذیر در #C :
نوشتن متدهایی که استفاده ازشون راحت و انعطافپذیر باشه، یکی از ویژگیهای کدنویسی حرفهایه. #C دو تا ابزار قدرتمند برای این کار در اختیار ما میذاره که به ما اجازه میده متدهایی با ورودیهای متغیر یا پیشفرض طراحی کنیم.
بیاید با تمام جزئیات، این دو قابلیت کلیدی رو کالبدشکافی کنیم.
کلمه کلیدی params به شما اجازه میده یه متد بنویسید که بتونه هر تعدادی آرگومان از یک نوع خاص رو به عنوان ورودی قبول کنه. این قابلیت برای متدهای کاربردی مثل توابع محاسباتی یا لاگرها فوقالعادهست.
جادوی پشت صحنه: کامپایلر #C به صورت خودکار، تمام آرگومانهای ورودی شما رو که با کاما جدا شدن، میگیره و اونها رو تبدیل به یه آرایه میکنه و به متد شما تحویل میده.
قانون 1️⃣: params فقط و فقط میتونه برای آخرین پارامتر یک متد استفاده بشه.
قانون 2️⃣: نوع اون پارامتر باید یک آرایه یکبعدی باشه.
این قابلیت به شما اجازه میده برای پارامترهای متدتون، یک مقدار پیشفرض تعریف کنید. اگه کاربر موقع صدا زدن متد، مقداری برای اون پارامتر نفرسته، از همون مقدار پیشفرض استفاده میشه.
تله کامپایلر! مقدار پیشفرض در محل صدا زدن متد (calling side) قرار میگیره، نه در خود متد. یعنی RegisterUser("Sara") بعد از کامپایل، به RegisterUser("Sara", "Iran") تبدیل میشه.
این دو قابلیت، ابزارهای شما برای طراحی APIهای تمیز و انعطافپذیر هستن.
از params وقتی استفاده کنید که تعداد ورودیها نامشخصه.
از پارامترهای اختیاری وقتی استفاده کنید که یه پارامتر، یه مقدار پیشفرض رایج داره.
شما از کدوم یکی از این دو قابلیت بیشتر استفاده میکنید؟ آیا تا حالا به تله کامپایلری پارامترهای اختیاری برخورد کرده بودید؟
نظراتتون رو کامنت کنید! 👇
[C# Geeks Hangout]
راهنمای کامل params و پارامترهای اختیاری
نوشتن متدهایی که استفاده ازشون راحت و انعطافپذیر باشه، یکی از ویژگیهای کدنویسی حرفهایه. #C دو تا ابزار قدرتمند برای این کار در اختیار ما میذاره که به ما اجازه میده متدهایی با ورودیهای متغیر یا پیشفرض طراحی کنیم.
بیاید با تمام جزئیات، این دو قابلیت کلیدی رو کالبدشکافی کنیم.
1️⃣params: پذیرش تعداد نامشخص آرگومان
کلمه کلیدی params به شما اجازه میده یه متد بنویسید که بتونه هر تعدادی آرگومان از یک نوع خاص رو به عنوان ورودی قبول کنه. این قابلیت برای متدهای کاربردی مثل توابع محاسباتی یا لاگرها فوقالعادهست.
جادوی پشت صحنه: کامپایلر #C به صورت خودکار، تمام آرگومانهای ورودی شما رو که با کاما جدا شدن، میگیره و اونها رو تبدیل به یه آرایه میکنه و به متد شما تحویل میده.
✨️قوانین کلیدی params:
قانون 1️⃣: params فقط و فقط میتونه برای آخرین پارامتر یک متد استفاده بشه.
قانون 2️⃣: نوع اون پارامتر باید یک آرایه یکبعدی باشه.
// با params، این متد هر تعداد int رو قبول میکنه
int Sum(params int[] numbers)
{
int total = 0;
// numbers در اینجا یک آرایه معمولی است
for (int i = 0; i < numbers.Length; i++)
{
total += numbers[i];
}
return total;
}
// --- نحوه استفاده ---
// شما میتونید هر تعداد آرگومان که خواستید، با کاما جدا کنید
int total1 = Sum(1, 2, 3, 4); // خروجی: 10
// یا حتی خودتون یه آرایه بهش پاس بدید
int[] myNumbers = { 10, 20, 30 };
int total2 = Sum(myNumbers); // خروجی: 60
2️⃣پارامترهای اختیاری: تعیین مقادیر پیشفرض
این قابلیت به شما اجازه میده برای پارامترهای متدتون، یک مقدار پیشفرض تعریف کنید. اگه کاربر موقع صدا زدن متد، مقداری برای اون پارامتر نفرسته، از همون مقدار پیشفرض استفاده میشه.
void RegisterUser(string username, string country = "Iran")
{
Console.WriteLine($"User: {username}, Country: {country}");
}
// --- نحوه استفاده ---
RegisterUser("Ali", "Germany"); // User: Ali, Country: Germany
RegisterUser("Sara"); // User: Sara, Country: Iran
نکات فنی و قوانین مهم:
تله کامپایلر! مقدار پیشفرض در محل صدا زدن متد (calling side) قرار میگیره، نه در خود متد. یعنی RegisterUser("Sara") بعد از کامپایل، به RegisterUser("Sara", "Iran") تبدیل میشه.
نتیجه مهم:
●اگه شما یه کتابخونه بسازید و بعداً مقدار پیشفرض رو توش عوض کنید، پروژههایی که از اون کتابخونه استفاده میکنن تا وقتی که دوباره کامپایل نشن، از همون مقدار پیشفرض قدیمی استفاده خواهند کرد!
●مقدار پیشفرض باید یک عبارت ثابت در زمان کامپایل باشه (مثل یک عدد، رشته، null یا default).
●پارامترهای اختیاری نمیتونن ref یا out باشن.
●در تعریف متد، پارامترهای اجباری باید قبل از پارامترهای اختیاری بیان.
🤔 حرف حساب و تجربه شما
این دو قابلیت، ابزارهای شما برای طراحی APIهای تمیز و انعطافپذیر هستن.
از params وقتی استفاده کنید که تعداد ورودیها نامشخصه.
از پارامترهای اختیاری وقتی استفاده کنید که یه پارامتر، یه مقدار پیشفرض رایج داره.
شما از کدوم یکی از این دو قابلیت بیشتر استفاده میکنید؟ آیا تا حالا به تله کامپایلری پارامترهای اختیاری برخورد کرده بودید؟
نظراتتون رو کامنت کنید! 👇
[C# Geeks Hangout]
🔖 هشتگها:
#Parameters
#OptionalParameters
#Params
🏷 خداحافظی با اشتباهات ترتیب پارامترها:
تا حالا شده یه متد با چند تا ورودی از یک نوع (مثلاً چند تا int یا bool) داشته باشید و موقع صدا زدنش، جای دو تا آرگومان رو با هم اشتباه بگیرید؟ یا ندونید هر مقدار مال کدوم پارامتره؟
😎سیشارپ با قابلیت آرگومانهای نامدار (Named Arguments) این مشکل رو برای همیشه حل کرده و خوانایی کد شما رو به شدت بالا میبره.
خیلی سادهست! به جای اینکه به ترتیب، فقط مقدارها رو به متد پاس بدید، میتونید اسم پارامتر رو بنویسید، بعد دو نقطه (:) و بعد مقدارش رو.
زیبایی این قابلیت اینه که وقتی از اسم پارامترها استفاده میکنید، دیگه ترتیب مهم نیست! میتونید اونها رو به هر ترتیبی که دوست دارید به متد پاس بدید. این کار، کد رو خیلی خواناتر میکنه.
شما میتونید آرگومانهای معمولی (بدون نام) و نامدار رو با هم ترکیب کنید، اما یه قانون مهم داره: آرگومانهای معمولی باید همیشه قبل از آرگومانهای نامدار بیان.
قدرت واقعی آرگومانهای نامدار، وقتی مشخص میشه که با پارامترهای اختیاری ترکیب بشن.
فرض کنید متدی با چندین پارامتر اختیاری دارید و فقط میخواید مقدار آخری رو تغییر بدید. بدون آرگومان نامدار، باید برای تمام پارامترهای قبلی هم مقدار پیشفرضشون رو دوباره بنویسید! اما با این قابلیت، کار خیلی ساده میشه:
آرگومانهای نامدار ابزار فوقالعادهای برای افزایش خوانایی و کاهش باگهای ناشی از ترتیب اشتباه پارامترها هستن، به خصوص در متدهایی با تعداد ورودی زیاد.
شما بیشتر کجا از Named Arguments استفاده میکنید؟ آیا این قابلیت به تمیزتر شدن کدهاتون کمک کرده؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[پاتوق گیک های #C]
قدرت Named Arguments در #C
تا حالا شده یه متد با چند تا ورودی از یک نوع (مثلاً چند تا int یا bool) داشته باشید و موقع صدا زدنش، جای دو تا آرگومان رو با هم اشتباه بگیرید؟ یا ندونید هر مقدار مال کدوم پارامتره؟
😎سیشارپ با قابلیت آرگومانهای نامدار (Named Arguments) این مشکل رو برای همیشه حل کرده و خوانایی کد شما رو به شدت بالا میبره.
1️⃣آرگومان نامدار چیست؟
خیلی سادهست! به جای اینکه به ترتیب، فقط مقدارها رو به متد پاس بدید، میتونید اسم پارامتر رو بنویسید، بعد دو نقطه (:) و بعد مقدارش رو.
void Foo(int x, int y)
{
Console.WriteLine($"x: {x}, y: {y}");
}
// صدا زدن متد با آرگومانهای نامدار
Foo(x: 1, y: 2); // خروجی: x: 1, y: 2
2️⃣قدرت اصلی: بینیازی از ترتیب!
زیبایی این قابلیت اینه که وقتی از اسم پارامترها استفاده میکنید، دیگه ترتیب مهم نیست! میتونید اونها رو به هر ترتیبی که دوست دارید به متد پاس بدید. این کار، کد رو خیلی خواناتر میکنه.
// این دو خط کد دقیقاً یک کار یکسان رو انجام میدن
Foo(x: 1, y: 2);
Foo(y: 2, x: 1);
3️⃣ترکیب با آرگومانهای معمولی(Positional) ⚠️
شما میتونید آرگومانهای معمولی (بدون نام) و نامدار رو با هم ترکیب کنید، اما یه قانون مهم داره: آرگومانهای معمولی باید همیشه قبل از آرگومانهای نامدار بیان.
// ✅ درسته: اول معمولی، بعد نامدار
Foo(1, y: 2);
// ❌ خطای کامپایل: نمیتونی بعد از نامدار، معمولی بیاری
// Foo(x: 1, 2); // این کار معمولاً توصیه نمیشه، هرچند اگه آرگومان در جایگاه درستش باشه، کامپایلر قبول میکنه
4️⃣بهترین کاربرد: پارامترهای اختیاری
قدرت واقعی آرگومانهای نامدار، وقتی مشخص میشه که با پارامترهای اختیاری ترکیب بشن.
فرض کنید متدی با چندین پارامتر اختیاری دارید و فقط میخواید مقدار آخری رو تغییر بدید. بدون آرگومان نامدار، باید برای تمام پارامترهای قبلی هم مقدار پیشفرضشون رو دوباره بنویسید! اما با این قابلیت، کار خیلی ساده میشه:
void Bar(int a = 0, int b = 0, int c = 0, int d = 0)
{
Console.WriteLine($"a:{a}, b:{b}, c:{c}, d:{d}");
}
// فقط پارامتر d رو مقداردهی میکنیم و بقیه پیشفرض باقی میمونن
Bar(d: 3); // خروجی: a:0, b:0, c:0, d:3
🤔 حرف حساب و تجربه شما
آرگومانهای نامدار ابزار فوقالعادهای برای افزایش خوانایی و کاهش باگهای ناشی از ترتیب اشتباه پارامترها هستن، به خصوص در متدهایی با تعداد ورودی زیاد.
شما بیشتر کجا از Named Arguments استفاده میکنید؟ آیا این قابلیت به تمیزتر شدن کدهاتون کمک کرده؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[پاتوق گیک های #C]
🔖 هشتگها:
#CSharp
#DotNet
#CleanCode
🔬 تکنیکهای پیشرفته #C :
آمادهاید که به بخشهای عمیق و کمتر دیدهشده #C سفر کنیم؟ امروز میخوایم در مورد دو تا قابلیت قدرتمند ولی خاص صحبت کنیم که برای بهینهسازیهای سطح پایین و کارهای حرفهای استفاده میشن: ref locals و ref returns.
اینها ابزارهای روزمره شما نیستن، ولی شناختنشون شما رو به درک عمیقتری از قدرت و انعطاف #C میرسونه.
1️⃣این ref local چیست؟ یک نام مستعار برای حافظه
یه ref local، یه متغیر محلیه که یک کپی از مقدار نیست، بلکه یک ارجاع یا نام مستعار مستقیم به حافظهی یک متغیر دیگهست (مثل یک عنصر آرایه یا یک فیلد).
این یعنی هر تغییری روی این متغیر، مستقیماً روی مقدار اصلی اعمال میشه!
2️⃣حالا ref return چیست؟ برگرداندن آدرس به جای مقدار 💥
یه متد میتونه به جای برگردوندن مقدار یک متغیر، رفرنس یا آدرس حافظهی اون رو برگردونه. این کار به کدی که متد رو صدا زده، اجازه دسترسی مستقیم و تغییر مقدار اصلی رو میده.
3️⃣نوبت ref readonly: ترکیب پرفورمنس و ایمنی 🛡
گاهی وقتا شما میخواید از مزیت پرفورمنس برگردوندن رفرنس (جلوگیری از کپی شدن دادههای بزرگ) استفاده کنید، ولی نمیخواید کدی که اون رو دریافت کرده، بتونه مقدار اصلی رو تغییر بده.
اینجاست که ref readonly وارد میشه. با این قابلیت، شما رفرنس رو برمیگردونید، ولی اون رفرنس فقط-خواندنی خواهد بود.
4️⃣هشدار و کاربرد اصلی ⚠️
این قابلیتها ابزارهای روزمره شما نیستن. اونها برای micro optimization های خاص طراحی شدن، به خصوص در سناریوهای پرفورمنس بالا مثل کار با <Span<T یا در بازیسازی که هر نانوثانیه مهمه.
در ۹۹٪ مواقع، شما به اینها نیاز ندارید و کد سادهتر، همیشه بهتره.
آشنایی با این مفاهیم، حتی اگه ازشون استفاده نکنید، دید شما رو نسبت به قابلیتهای سطح پایین و قدرت #C عمیقتر میکنه.
آیا تا حالا از ref locals یا ref returns تو پروژهای استفاده کردید؟ یا اصلاً از وجودشون خبر داشتید؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[C# Geeks Hangout]
شیرجه عمیق در ref locals و ref returns
آمادهاید که به بخشهای عمیق و کمتر دیدهشده #C سفر کنیم؟ امروز میخوایم در مورد دو تا قابلیت قدرتمند ولی خاص صحبت کنیم که برای بهینهسازیهای سطح پایین و کارهای حرفهای استفاده میشن: ref locals و ref returns.
اینها ابزارهای روزمره شما نیستن، ولی شناختنشون شما رو به درک عمیقتری از قدرت و انعطاف #C میرسونه.
1️⃣این ref local چیست؟ یک نام مستعار برای حافظه
یه ref local، یه متغیر محلیه که یک کپی از مقدار نیست، بلکه یک ارجاع یا نام مستعار مستقیم به حافظهی یک متغیر دیگهست (مثل یک عنصر آرایه یا یک فیلد).
این یعنی هر تغییری روی این متغیر، مستقیماً روی مقدار اصلی اعمال میشه!
int[] numbers = { 0, 1, 2, 3, 4 };
// numRef حالا یک نام دیگر برای numbers[2] است
ref int numRef = ref numbers[2];
// هر تغییری روی numRef، مستقیماً روی عنصر آرایه اعمال میشه
numRef *= 10;
Console.WriteLine(numRef); // خروجی: 20
Console.WriteLine(numbers[2]); // خروجی: 20 (عنصر اصلی هم تغییر کرد!)2️⃣حالا ref return چیست؟ برگرداندن آدرس به جای مقدار 💥
یه متد میتونه به جای برگردوندن مقدار یک متغیر، رفرنس یا آدرس حافظهی اون رو برگردونه. این کار به کدی که متد رو صدا زده، اجازه دسترسی مستقیم و تغییر مقدار اصلی رو میده.
class Program
{
static string x = "Old Value";
// این متد، رفرنس به فیلد x رو برمیگردونه
static ref string GetX() => ref x;
static void Main()
{
// xRef یک رفرنس مستقیم به x میشه
ref string xRef = ref GetX();
// تغییر xRef، خودِ متغیر استاتیک x رو تغییر میده
xRef = "New Value";
Console.WriteLine(x); // خروجی: New Value
}
}
3️⃣نوبت ref readonly: ترکیب پرفورمنس و ایمنی 🛡
گاهی وقتا شما میخواید از مزیت پرفورمنس برگردوندن رفرنس (جلوگیری از کپی شدن دادههای بزرگ) استفاده کنید، ولی نمیخواید کدی که اون رو دریافت کرده، بتونه مقدار اصلی رو تغییر بده.
اینجاست که ref readonly وارد میشه. با این قابلیت، شما رفرنس رو برمیگردونید، ولی اون رفرنس فقط-خواندنی خواهد بود.
4️⃣هشدار و کاربرد اصلی ⚠️
این قابلیتها ابزارهای روزمره شما نیستن. اونها برای micro optimization های خاص طراحی شدن، به خصوص در سناریوهای پرفورمنس بالا مثل کار با <Span<T یا در بازیسازی که هر نانوثانیه مهمه.
در ۹۹٪ مواقع، شما به اینها نیاز ندارید و کد سادهتر، همیشه بهتره.
🤔 حرف حساب و تجربه شما
آشنایی با این مفاهیم، حتی اگه ازشون استفاده نکنید، دید شما رو نسبت به قابلیتهای سطح پایین و قدرت #C عمیقتر میکنه.
آیا تا حالا از ref locals یا ref returns تو پروژهای استفاده کردید؟ یا اصلاً از وجودشون خبر داشتید؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[C# Geeks Hangout]
🔖 هشتگها:
#Ref
#CSharp
#AdvancedCSharp
🔬 کالبدشکافی var در #C :
کلمه کلیدی var یکی از مدرنترین و در عین حال بحثبرانگیزترین قابلیتهای #C هست. بعضیها عاشقشن و بعضیها ازش فراریان.
اما var دقیقاً چیه؟ آیا #C رو مثل جاوااسکریپت، داینامیک میکنه؟ بیاید یک بار برای همیشه، این ابزار قدرتمند رو کالبدشکافی کنیم.
این var یه کلمه کلیدی برای راحتی کاره. وقتی از var استفاده میکنید، به کامپایلر میگید:
"من نوع این متغیر محلی رو نمینویسم. خودت از روی عبارتی که دارم بهش اختصاص میدم، نوعش رو استنتاج (Infer) کن."
این کدها دقیقاً معادل این هستن که شما خودتون نوع رو به صورت صریح بنویسید.
این بزرگترین تصور اشتباه در مورد var هست. استفاده از var به هیچ وجه #C رو به یه زبان داینامیک تبدیل نمیکنه.
نوع متغیری که با var تعریف میشه، در زمان کامپایل مشخص و قفل میشه و دیگه هرگز قابل تغییر نیست. var فقط برای خلاصهنویسیه.
با اینکه var کد رو کوتاهتر میکنه، گاهی وقتا میتونه خوانایی کد رو کم کنه. قانون کلی اینه: اگه با یه نگاه به سمت راست مساوی، نتونستید نوع متغیر رو به راحتی تشخیص بدید، بهتره از var استفاده نکنید.
کلمه کلیدی var یه ابزار عالی برای خلاصهنویسی و تمیزتر کردن کده، به شرطی که هوشمندانه ازش استفاده بشه و خوانایی رو فدای کوتاهی نکنیم.
شما از طرفدارای var هستید یا مخالفش؟ به نظرتون کجاها استفاده از var کد رو بهتر میکنه و کجاها بدتر؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[پاتوق گیک های #C]
ابزار قدرتمند یا کدنویسی شلخته؟
کلمه کلیدی var یکی از مدرنترین و در عین حال بحثبرانگیزترین قابلیتهای #C هست. بعضیها عاشقشن و بعضیها ازش فراریان.
اما var دقیقاً چیه؟ آیا #C رو مثل جاوااسکریپت، داینامیک میکنه؟ بیاید یک بار برای همیشه، این ابزار قدرتمند رو کالبدشکافی کنیم.
1️⃣حالا var چیست؟ استنتاج نوع در زمان کامپایل 🤖
این var یه کلمه کلیدی برای راحتی کاره. وقتی از var استفاده میکنید، به کامپایلر میگید:
"من نوع این متغیر محلی رو نمینویسم. خودت از روی عبارتی که دارم بهش اختصاص میدم، نوعش رو استنتاج (Infer) کن."
// کامپایلر میفهمه که x از نوع string هست
var x = "hello";
// کامپایلر میفهمه که y از نوع StringBuilder هست
var y = new System.Text.StringBuilder();
// کامپایلر میفهمه که z از نوع float هست
var z = (float)Math.PI;
این کدها دقیقاً معادل این هستن که شما خودتون نوع رو به صورت صریح بنویسید.
2️⃣مهمترین نکته: var داینامیک نیست! (Statically Typed) 🛡
این بزرگترین تصور اشتباه در مورد var هست. استفاده از var به هیچ وجه #C رو به یه زبان داینامیک تبدیل نمیکنه.
نوع متغیری که با var تعریف میشه، در زمان کامپایل مشخص و قفل میشه و دیگه هرگز قابل تغییر نیست. var فقط برای خلاصهنویسیه.
// اینجا کامپایلر نوع x رو int تشخیص میده و این نوع برای همیشه ثابت میمونه
var x = 5;
// ❌ خطای زمان کامپایل!
// شما نمیتونید یه رشته رو تو یه متغیر از نوع int بریزید.
x = "hello";
3️⃣تله خوانایی: کی نباید از var استفاده کنیم؟ 🧐
با اینکه var کد رو کوتاهتر میکنه، گاهی وقتا میتونه خوانایی کد رو کم کنه. قانون کلی اینه: اگه با یه نگاه به سمت راست مساوی، نتونستید نوع متغیر رو به راحتی تشخیص بدید، بهتره از var استفاده نکنید.
// اینجا x از چه نوعیه؟ int? long?
// برای فهمیدنش باید بریم و متد Next رو چک کنیم.
// این کد خوانایی خوبی نداره.
var x = new Random().Next();
// نوشتن صریح، اینجا خیلی بهتر و خواناتره:
int x = new Random().Next();
🤔 حرف حساب و استایل شما
کلمه کلیدی var یه ابزار عالی برای خلاصهنویسی و تمیزتر کردن کده، به شرطی که هوشمندانه ازش استفاده بشه و خوانایی رو فدای کوتاهی نکنیم.
نکته: سناریوهایی هم وجود داره (مثل کار با Anonymous Types) که استفاده از var در اونها اجباریه و بعداً بهشون میرسیم.
شما از طرفدارای var هستید یا مخالفش؟ به نظرتون کجاها استفاده از var کد رو بهتر میکنه و کجاها بدتر؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[پاتوق گیک های #C]
🔖 هشتگها:
#CSharp
#CodingTips #CleanCode #Var