📚 ساختار کلی برنامه #C
کدها توی #C به صورت بیانها (Statements) نوشته میشن که با ; تموم میشن.
متدها مثل WriteLine توابعی هستن که داخل کلاسها قرار دارن. کلاسها هم کدها و دادهها رو دستهبندی میکنن.
کلاس Console مخصوص کار با ورودی و خروجی خط فرمانه و تو فضای نام System قرار داره.
🌐 فضاهای نام (Namespace)
فضای نام System شامل کلاسهای پایه مثل Console هست.
استفاده از System.Console تو هر بار صدا زدن میتونه خستهکننده باشه. برای همین میتونیم بالای کد این خط رو اضافه کنیم:
و بعدش سادهتر بنویسیم:
کدها توی #C به صورت بیانها (Statements) نوشته میشن که با ; تموم میشن.
متدها مثل WriteLine توابعی هستن که داخل کلاسها قرار دارن. کلاسها هم کدها و دادهها رو دستهبندی میکنن.
کلاس Console مخصوص کار با ورودی و خروجی خط فرمانه و تو فضای نام System قرار داره.
🌐 فضاهای نام (Namespace)
فضای نام System شامل کلاسهای پایه مثل Console هست.
استفاده از System.Console تو هر بار صدا زدن میتونه خستهکننده باشه. برای همین میتونیم بالای کد این خط رو اضافه کنیم:
using System;
و بعدش سادهتر بنویسیم:
Console.WriteLine(x);
🧱 بازنویسی برنامه با متد شخصی:
میتونیم کد رو حرفهایتر کنیم و یه متد بنویسیم که این محاسبه رو انجام بده:
✅ این متد FeetToInches یه ورودی میگیره و خروجی برمیگردونه. ورودی اسمش feet و خروجی هم تعداد اینچهاست.
میتونیم کد رو حرفهایتر کنیم و یه متد بنویسیم که این محاسبه رو انجام بده:
using System;
Console.WriteLine(FeetToInches(30)); // 360
Console.WriteLine(FeetToInches(100)); // 1200
int FeetToInches(int feet)
{
int inches = feet * 12;
return inches;
}
✅ این متد FeetToInches یه ورودی میگیره و خروجی برمیگردونه. ورودی اسمش feet و خروجی هم تعداد اینچهاست.
😎بدون ورودی و خروجی؟ مشکلی نیست!
مثلاً این مثال ساده:
مثلاً این مثال ساده:
using System;
SayHello();
void SayHello()
{
Console.WriteLine("Hello, world");
}
دستور ساخت پروژه کنسول جدید:
برای اجرای برنامه از پوشه پروژه:
یا فقط کامپایل بدون اجرا:
💬 نگران نباش! اگه الان چیزی از این کدها نفهمیدی اصلاً اشکال نداره! 😎 چون تو پستهای بعدی، دونه دونه همهی این بخشها رو بهزبون ساده توضیح میدیم و با مثالهای بیشتر یاد میگیری.
dotnet new console -n ProgramName
برای اجرای برنامه از پوشه پروژه:
dotnet run ProgramName
یا فقط کامپایل بدون اجرا:
dotnet build ProgramName
.csproj
💬 نگران نباش! اگه الان چیزی از این کدها نفهمیدی اصلاً اشکال نداره! 😎 چون تو پستهای بعدی، دونه دونه همهی این بخشها رو بهزبون ساده توضیح میدیم و با مثالهای بیشتر یاد میگیری.
🔖 هشتگها :
#اولین_برنامه
#CSharp_پایه
#برنامه_نویسی
🚀 ساختار زبان برنامه نویسی (Syntax) در #C :
سلام به همه گیکهای #C 🤓 تا حالا به این فکر کردید که چطور زبان #C کدهای ما رو میفهمه و اجرا میکنه؟ همه چیز برمیگرده به Syntax!
در این پست، میخوایم عناصر اساسی سینتکس #C رو بررسی کنیم که از C و ++C الهام گرفته.
بیاین با این تکه کد ساده شروع کنیم:
این چند خط، دنیایی از مفاهیم سینتکسی رو در خودشون جا دادن!
سلام به همه گیکهای #C 🤓 تا حالا به این فکر کردید که چطور زبان #C کدهای ما رو میفهمه و اجرا میکنه؟ همه چیز برمیگرده به Syntax!
در این پست، میخوایم عناصر اساسی سینتکس #C رو بررسی کنیم که از C و ++C الهام گرفته.
بیاین با این تکه کد ساده شروع کنیم:
using System;
int x = 12 * 30;
Console.WriteLine (x);
این چند خط، دنیایی از مفاهیم سینتکسی رو در خودشون جا دادن!
--شناسهها(Identifiers):
نامهایی هستند که ما برنامهنویسها انتخاب میکنیم. مثلاً در کد بالا، System، x، Console و WriteLine همگی شناسه هستند.
-- قواعد مهم: به بزرگی و کوچکی حروف حساساند (case-sensitive).
--کنوانسیون نامگذاری:
برای پارامترها، متغیرهای محلی و فیلدهای خصوصی.
برای بقیه شناسهها (کلاسها، متدها، Propertyها).
--کلمات کلیدی(Keywords):
واژههایی هستند که برای کامپایلر معنی خاص و از پیش تعریف شدهای دارن. در مثال ما، using و int کلمات کلیدی هستند.
--کلمات کلیدی رزرو شده: بیشتر کلمات کلیدی #C رزرو شدهاند و نمیتوانید از آنها به عنوان شناسهی معمولی استفاده کنید. (مثل class, public, if, for و ...).
کلمات کلیدی متنی(Contextual Keywords):
یک سری کلمات کلیدی هستند که در برخی زمینهها معنی خاصی دارند ولی میتوانید از آنها به عنوان شناسه هم استفاده کنید، چون ابهامی ایجاد نمیکنند (مثل var, async, await, yield).
نامهایی هستند که ما برنامهنویسها انتخاب میکنیم. مثلاً در کد بالا، System، x، Console و WriteLine همگی شناسه هستند.
-- قواعد مهم: به بزرگی و کوچکی حروف حساساند (case-sensitive).
--کنوانسیون نامگذاری:
Camel Case (myLocalVariable)
برای پارامترها، متغیرهای محلی و فیلدهای خصوصی.
Pascal Case (MyLocakMethod)
برای بقیه شناسهها (کلاسها، متدها، Propertyها).
--کلمات کلیدی(Keywords):
واژههایی هستند که برای کامپایلر معنی خاص و از پیش تعریف شدهای دارن. در مثال ما، using و int کلمات کلیدی هستند.
--کلمات کلیدی رزرو شده: بیشتر کلمات کلیدی #C رزرو شدهاند و نمیتوانید از آنها به عنوان شناسهی معمولی استفاده کنید. (مثل class, public, if, for و ...).
نکته باحال : اگر واقعاً مجبور شدید، میتوانید با قرار دادن @ قبل از کلمه کلیدی، از آن به عنوان شناسه استفاده کنید:
int @using = 123;
کلمات کلیدی متنی(Contextual Keywords):
یک سری کلمات کلیدی هستند که در برخی زمینهها معنی خاصی دارند ولی میتوانید از آنها به عنوان شناسه هم استفاده کنید، چون ابهامی ایجاد نمیکنند (مثل var, async, await, yield).
--لیترالها(Literals):
دادههای خام و ثابتی که مستقیماً در کد وارد میکنیم. در مثال بالا، 12 و 30 لیترالهای عددی هستند.
--علامتهای نگارشی(Punctuators):
به ساختار و خوانایی کد کمک میکنند. بارزترین مثال، سمیکالن (;) است که یک دستور رو به پایان میرسونه.
--عملگرها (Operators):
عملیات خاصی روی مقادیر(Operands) انجام میدن. در کد مثال ما، این عملگرها رو داریم:
دادههای خام و ثابتی که مستقیماً در کد وارد میکنیم. در مثال بالا، 12 و 30 لیترالهای عددی هستند.
--علامتهای نگارشی(Punctuators):
به ساختار و خوانایی کد کمک میکنند. بارزترین مثال، سمیکالن (;) است که یک دستور رو به پایان میرسونه.
--عملگرها (Operators):
عملیات خاصی روی مقادیر(Operands) انجام میدن. در کد مثال ما، این عملگرها رو داریم:
= : عملگر انتساب (Assignment) که یک مقدار رو به یک متغیر اختصاص میده.
* : عملگر ضرب (Multiplication).
. : عملگر عضو (Member Access) برای دسترسی به اعضای یک کلاس یا شیء (مثل Console.WriteLine).
() : پرانتزها که برای فراخوانی متدها استفاده میشن. اگه متدی آرگومان نگیره، پرانتزها خالی میمونن.
🔖 هشتگها : #Syntax
📝 کامنتگذاری در #C:
سلام به گیکهای منظم و آیندهنگر!🧑💻
وقتی داریم کد مینویسیم، ممکنه یه جاهایی نیاز داشته باشیم توضیحاتی برای خودمون یا همتیمیهامون بذاریم. اینجاست که کامنتها (Comments) وارد عمل میشن! کامنتها خطوطی از کد هستن که کامپایلر #C اصلاً بهشون کاری نداره و فقط برای ما برنامهنویسها معنی دارن تا کد رو بهتر درک کنیم.
سی شارپ دو مدل کامنتگذاری اصلی و پرکاربرد داره که بهمون کمک میکنه کدمون همیشه خوانا و قابل فهم باشه:
سلام به گیکهای منظم و آیندهنگر!🧑💻
وقتی داریم کد مینویسیم، ممکنه یه جاهایی نیاز داشته باشیم توضیحاتی برای خودمون یا همتیمیهامون بذاریم. اینجاست که کامنتها (Comments) وارد عمل میشن! کامنتها خطوطی از کد هستن که کامپایلر #C اصلاً بهشون کاری نداره و فقط برای ما برنامهنویسها معنی دارن تا کد رو بهتر درک کنیم.
سی شارپ دو مدل کامنتگذاری اصلی و پرکاربرد داره که بهمون کمک میکنه کدمون همیشه خوانا و قابل فهم باشه:
1️⃣ کامنتهای تکخطی:
کاربرد: بهترین گزینه برای توضیح یک خط کد
خاص، یا ارائه یک نکته کوتاه و مختصر.
نحوه استفاده: با دو تا اسلش رو به جلو (//) شروع میشه و تا پایان همون خط ادامه پیدا میکنه.
مثال کاربردی:
این کامنتها برای وقتی که میخواید سریعاً یه توضیح کوچیک کنار کدتون بنویسید، فوقالعادهان!
(Single-Line Comments) //
کاربرد: بهترین گزینه برای توضیح یک خط کد
خاص، یا ارائه یک نکته کوتاه و مختصر.
نحوه استفاده: با دو تا اسلش رو به جلو (//) شروع میشه و تا پایان همون خط ادامه پیدا میکنه.
مثال کاربردی:
int counter = 0; // This is a Comment
این کامنتها برای وقتی که میخواید سریعاً یه توضیح کوچیک کنار کدتون بنویسید، فوقالعادهان!
2️⃣ کامنتهای چندخطی:
کاربرد: ایدهآل برای توضیحات طولانیتر، شرح دادن یک بلوک کد بزرگ، یا حتی غیرفعال کردن موقتی چند خط کد.
نحوه استفاده: با /* شروع میشه و با */ به پایان میرسه و میتونه شامل چندین خط باشه.
مثال کاربردیتر:
این کامنتها زمانی به کار میان که نیاز به توضیح عمیقتر یا خلاصهای از عملکرد یک بخش بزرگتر از کد دارید.
(Multiline Comments) /* ... */
کاربرد: ایدهآل برای توضیحات طولانیتر، شرح دادن یک بلوک کد بزرگ، یا حتی غیرفعال کردن موقتی چند خط کد.
نحوه استفاده: با /* شروع میشه و با */ به پایان میرسه و میتونه شامل چندین خط باشه.
مثال کاربردیتر:
/*
این بخش از کد مسئول محاسبه
مجموع اعداد ۱ تا ۱۰ است.
از یک حلقه for برای این کار استفاده شده.
*/
int sum = 0;
for (int i = 1; i <= 10; i++)
{
sum += i;
}
این کامنتها زمانی به کار میان که نیاز به توضیح عمیقتر یا خلاصهای از عملکرد یک بخش بزرگتر از کد دارید.
💡نکته حرفهای:
کامنتگذاری هوشمندانه، نه تنها کد شما رو برای بقیه (و خودتون در آینده!) مثل روز روشن میکنه، بلکه یک مستندسازی داخلی ارزشمند برای پروژههای بزرگ محسوب میشه. سعی کنید کامنتها رو همیشه بهروز نگه دارید و از کامنتهای تکراری و بیفایده پرهیز کنید.
کامنتگذاری هوشمندانه، نه تنها کد شما رو برای بقیه (و خودتون در آینده!) مثل روز روشن میکنه، بلکه یک مستندسازی داخلی ارزشمند برای پروژههای بزرگ محسوب میشه. سعی کنید کامنتها رو همیشه بهروز نگه دارید و از کامنتهای تکراری و بیفایده پرهیز کنید.
🔖 هشتگها : #Comments
💎 فوت و فن انواع داده (Types) در #C :
از صفر تا کلاسهای سفارشی
هر برنامهنویس کاردرستی میدونه که برای نوشتن کدهای قدرتمند و تمیز، اول باید به مفهوم «انواع داده» (Types) مسلط شد. این مفهوم، مثل DNA برای دادههای ما عمل میکنه.
فکر کنید هر Type، شناسنامهی یه مقداره. این شناسنامه به #C میگه که این داده دقیقاً چیه، چه چیزایی تو خودش نگه میداره و چه کارایی از دستش برمیاد.
مثلاً، متغیر (Variable) رو داریم که مثل یه گاوصندوقه؛ هر لحظه میتونیم یه مقدار جدید توش بذاریم. در مقابل، ثابت (Constant) مثل یه سنگنوشتهست؛ یه بار که تعریف شد، تا ابد همون باقی میمونه!
نکته طلایی: 💡 در #C، هیچ مقداری بیهویت نیست. هر مقدار یک Type دارد که شخصیت و رفتارش را شکل میدهد.
از صفر تا کلاسهای سفارشی
هر برنامهنویس کاردرستی میدونه که برای نوشتن کدهای قدرتمند و تمیز، اول باید به مفهوم «انواع داده» (Types) مسلط شد. این مفهوم، مثل DNA برای دادههای ما عمل میکنه.
فکر کنید هر Type، شناسنامهی یه مقداره. این شناسنامه به #C میگه که این داده دقیقاً چیه، چه چیزایی تو خودش نگه میداره و چه کارایی از دستش برمیاد.
مثلاً، متغیر (Variable) رو داریم که مثل یه گاوصندوقه؛ هر لحظه میتونیم یه مقدار جدید توش بذاریم. در مقابل، ثابت (Constant) مثل یه سنگنوشتهست؛ یه بار که تعریف شد، تا ابد همون باقی میمونه!
// 'x' یه متغیر از نوع int است.
int x = 12 * 30;
Console.WriteLine(x); // 360
// 'y' یک ثابت است که مقدارش غیرقابل تغییره.
const int y = 360;
نکته طلایی: 💡 در #C، هیچ مقداری بیهویت نیست. هر مقدار یک Type دارد که شخصیت و رفتارش را شکل میدهد.
🧰 ابزارهای آماده در جعبهابزار #C :
سیشارپ یه سری Type آماده و پرکاربرد رو از اول در اختیارمون گذاشته که آچار فرانسهی ما هستن. این سه تا از مهمترینهاشونن:
نماینده اعداد صحیح. هرجا به محاسبات، شمارندهها یا هر نوع عملیات ریاضی نیاز دارید، int اولین و بهترین انتخاب شماست.
برای نمایش و دستکاری هر نوع داده متنی، از نام کاربری گرفته تا یک پیام طولانی. قدرت واقعی string در متدهای بینظیرشه.
این نوع فقط دو حالت میشناسه: true یا false. تمام تصمیمهای پیچیده برنامه، از بررسی اجازه دسترسی گرفته تا فعال یا غیرفعال کردن یک ویژگی، زیر سر همین bool هست.
سیشارپ یه سری Type آماده و پرکاربرد رو از اول در اختیارمون گذاشته که آچار فرانسهی ما هستن. این سه تا از مهمترینهاشونن:
int: سلطان اعداد
نماینده اعداد صحیح. هرجا به محاسبات، شمارندهها یا هر نوع عملیات ریاضی نیاز دارید، int اولین و بهترین انتخاب شماست.
int soldItems = 99;
int stock = 1000 - soldItems;
string: استاد کار با متن
برای نمایش و دستکاری هر نوع داده متنی، از نام کاربری گرفته تا یک پیام طولانی. قدرت واقعی string در متدهای بینظیرشه.
string rawEmail = " TestUser@example.com ";
// فضای خالی رو حذف و همه رو به حروف کوچک تبدیل میکنه
string cleanEmail = rawEmail.Trim().ToLower();
Console.WriteLine(cleanEmail); // testuser@example.com
bool: فرماندهی منطق برنامه
این نوع فقط دو حالت میشناسه: true یا false. تمام تصمیمهای پیچیده برنامه، از بررسی اجازه دسترسی گرفته تا فعال یا غیرفعال کردن یک ویژگی، زیر سر همین bool هست.
bool hasPermission = true;
if (hasPermission)
{
Console.WriteLine("Access Granted");
}
🏗 اینجا قضیه جدی میشه: ساختن Typeهای شخصی!
قدرت واقعی شما به عنوان یه توسعهدهنده، زمانی شکوفا میشه که دیگه فقط از ابزارهای آماده استفاده نمیکنید، بلکه با استفاده از class، ابزارهای تخصصی خودتون رو میسازید. اینجا شما خالق هستید.
مثلاً، فرض کنید یه مبدل واحد نیاز داریم. به جای اینکه فرمول تبدیل رو همه جای کد پخش کنیم، یک بار برای همیشه یه class براش میسازیم.
ببینید چقدر تمیز و حرفهای ازش استفاده میکنیم:
و اینم نقشهی ساخت اون ابزار قدرتمند:
قدرت واقعی شما به عنوان یه توسعهدهنده، زمانی شکوفا میشه که دیگه فقط از ابزارهای آماده استفاده نمیکنید، بلکه با استفاده از class، ابزارهای تخصصی خودتون رو میسازید. اینجا شما خالق هستید.
مثلاً، فرض کنید یه مبدل واحد نیاز داریم. به جای اینکه فرمول تبدیل رو همه جای کد پخش کنیم، یک بار برای همیشه یه class براش میسازیم.
ببینید چقدر تمیز و حرفهای ازش استفاده میکنیم:
var feetToInchesConverter = new UnitConverter(12);
var milesToFeetConverter = new UnitConverter(5280);
int lengthInInches = feetToInchesConverter.Convert(30);
Console.WriteLine($"30 feet is {lengthInInches} inches."); // 30 feet is 360 inches.
و اینم نقشهی ساخت اون ابزار قدرتمند:
public class UnitConverter
{
// فیلد داخلی برای نگهداری منطق
private readonly int ratio;
// با این تابع، بهش میگیم با چه فرمولی کار کنه
public UnitConverter(int unitRatio)
{
this.ratio = unitRatio;
}
// و اینم خودِ عملیات تبدیل که انجام میده
public int Convert(int unit)
{
return unit * this.ratio;
}
}
🥊 دوئل حافظه در #C :
تا حالا شده یه متغیر رو جایی عوض کنی و ببینی یه جای دیگه هم ناخواسته تغییر کرده؟ یا برعکس، انتظار داشتی تغییر کنه ولی نکرده؟ ریشه ۹۰٪ این مشکلات، درک نکردن تفاوت بین Value Type و Reference Type هست.
این بحث فقط یه تئوری خشک و خالی نیست، بلکه کلید نوشتن کدهای بدون باگ و قابل پیشبینیه. بزن بریم این راز بزرگ رو کشف کنیم!
فکر کنید Value Type مثل یه تیکه کاغذه. وقتی اون رو به متغیر دیگهای میدین، یه کپی کامل ازش گرفته میشه. حالا هر بلایی سر اون کپی بیاد، کاغذ اصلی شما صحیح و سالمه!
شامل چه چیزایی میشه؟ تمام انواع عددی (int, double و...)، bool، char و مهمتر از همه، structها.
بیاین با مثال Point که به صورت struct تعریف شده ببینیم:
همونطور که میبینید، p2 زندگی خودشو داره و از تغییر p1 خبردار هم نمیشه!
حالا فکر کنید Reference Type مثل کلید یه خونهست. شما خودِ خونه رو به کسی نمیدین، فقط یه کلید ازش کپی میکنید و بهش میدین. هر کسی که کلید رو داشته باشه، میتونه بره داخل و دکوراسیون رو عوض کنه و این تغییر برای همه اونایی که کلید دارن، قابل مشاهدهست!
شامل چه چیزایی میشه؟ تمام classها، string، آرایهها، delegateها و interfaceها.
حالا همون مثال Point رو این بار به صورت class تعریف میکنیم:
اینجا دیگه p1 و p2 شریک جرم هستن! تغییر روی یکی، روی اون یکی هم تأثیر میذاره.
Value Types در برابر Reference Types
تا حالا شده یه متغیر رو جایی عوض کنی و ببینی یه جای دیگه هم ناخواسته تغییر کرده؟ یا برعکس، انتظار داشتی تغییر کنه ولی نکرده؟ ریشه ۹۰٪ این مشکلات، درک نکردن تفاوت بین Value Type و Reference Type هست.
این بحث فقط یه تئوری خشک و خالی نیست، بلکه کلید نوشتن کدهای بدون باگ و قابل پیشبینیه. بزن بریم این راز بزرگ رو کشف کنیم!
Value Types: کپی برابر اصل
فکر کنید Value Type مثل یه تیکه کاغذه. وقتی اون رو به متغیر دیگهای میدین، یه کپی کامل ازش گرفته میشه. حالا هر بلایی سر اون کپی بیاد، کاغذ اصلی شما صحیح و سالمه!
شامل چه چیزایی میشه؟ تمام انواع عددی (int, double و...)، bool، char و مهمتر از همه، structها.
بیاین با مثال Point که به صورت struct تعریف شده ببینیم:
public struct Point { public int X, Y; }
Point p1 = new Point();
p1.X = 7;
// این خط، یک کپی کامل از p1 در p2 ایجاد میکنه
Point p2 = p1;
// حالا p1 و p2 دو موجودیت کاملاً جدا در حافظه هستن
Console.WriteLine($"p1.X: {p1.X}, p2.X: {p2.X}");
// p1.X: 7
// p2.X: 7
// اگه p1 رو تغییر بدیم، هیچ تأثیری روی p2 نداره
p1.X = 9;
Console.WriteLine($"p1.X: {p1.X}, p2.X: {p2.X}");
// p1.X: 9
// p2.X: 7همونطور که میبینید، p2 زندگی خودشو داره و از تغییر p1 خبردار هم نمیشه!
Reference Types: یک آدرس، چند نفر
حالا فکر کنید Reference Type مثل کلید یه خونهست. شما خودِ خونه رو به کسی نمیدین، فقط یه کلید ازش کپی میکنید و بهش میدین. هر کسی که کلید رو داشته باشه، میتونه بره داخل و دکوراسیون رو عوض کنه و این تغییر برای همه اونایی که کلید دارن، قابل مشاهدهست!
شامل چه چیزایی میشه؟ تمام classها، string، آرایهها، delegateها و interfaceها.
حالا همون مثال Point رو این بار به صورت class تعریف میکنیم:
public class Point { public int X, Y; }
// ...
Point p1 = new Point();
p1.X = 7;
// این خط، فقط آدرس (رفرنس) p1 رو در p2 کپی میکنه
// حالا هر دو به یک آبجکت در حافظه اشاره دارن
Point p2 = p1;
Console.WriteLine($"p1.X: {p1.X}, p2.X: {p2.X}");
// p1.X: 7
// p2.X: 7
// اگه p1 رو تغییر بدیم، چون p2 هم به همون خونه نگاه میکنه، اونم تغییر رو میبینه
p1.X = 9;
Console.WriteLine($"p1.X: {p1.X}, p2.X: {p2.X}");
// p1.X: 9,
// p2.X: 9اینجا دیگه p1 و p2 شریک جرم هستن! تغییر روی یکی، روی اون یکی هم تأثیر میذاره.
🔖 هشتگها : #MemoryManagement
⚠️ خطرناکترین کلمه در #C :
اگه یه جایزهای برای منفورترین و رایجترین خطا تو دنیای داتنت وجود داشت، بدون شک به NullReferenceException میرسید. این خطا مثل یه روح سرگردانه که هر لحظه ممکنه ظاهر بشه و برنامهتون رو از کار بندازه. اما ریشه این کابوس، فقط یه کلمهست: null.
بزن بریم بفهمیم این null چیه و چطور از فاجعه جلوگیری کنیم.
👻 null دقیقاً یعنی چی؟
فکر کنید یه متغیر از نوع Reference Type (مثل یه class)، یه کارت ویزیته که روش آدرس یه خونه نوشته شده.
"null"
یعنی اون کارت ویزیت کاملاً سفیده و هیچ آدرسی روش نیست. پوچه! این رفرنس به هیچ آبجکتی در حافظه اشاره نمیکنه.
💥 فاجعه کجا رخ میده؟ (NullReferenceException)
حالا اگه شما سعی کنید با اون کارت ویزیت سفید، درِ خونه رو باز کنید (یعنی به یکی از اعضای اون آبجکت مثل X دسترسی پیدا کنید)، برنامه قاطی میکنه و با تمام قدرت فریاد میزنه: NullReferenceException!
ترجمه این خطا به زبون خودمونی اینه: "داری سعی میکنی از چیزی استفاده کنی که اصلاً وجود نداره!"
🚫 تکلیف Value Type ها چیه؟
خبر خوب اینه که Value Type ها (مثل int, struct, bool) ذاتاً نمیتونن null باشن. اونا همیشه یه مقداری دارن (حتی اگه مقدار پیشفرضشون صفر باشه).
Value Type مثل یه سکه هست که یا این روئه یا اون رو؛ حالت "هیچی" نداره.
🤔 حرف حساب و تکنیک شما چیه؟
مقابله با null یه مهارت روزمرهست. از چک کردنهای ساده مثل if (myVar != null) گرفته تا تکنیکهای مدرنتر در C#.
استراتژی شما برای جلوگیری از این خطای رو مخ چیه؟ آیا از Null-conditional operator (?.) و Null-coalescing operator (??) زیاد استفاده میکنید؟ یا روش خاصی برای مدیریت null تو کدهاتون دارید؟
همه چیز درباره null
اگه یه جایزهای برای منفورترین و رایجترین خطا تو دنیای داتنت وجود داشت، بدون شک به NullReferenceException میرسید. این خطا مثل یه روح سرگردانه که هر لحظه ممکنه ظاهر بشه و برنامهتون رو از کار بندازه. اما ریشه این کابوس، فقط یه کلمهست: null.
بزن بریم بفهمیم این null چیه و چطور از فاجعه جلوگیری کنیم.
👻 null دقیقاً یعنی چی؟
فکر کنید یه متغیر از نوع Reference Type (مثل یه class)، یه کارت ویزیته که روش آدرس یه خونه نوشته شده.
"null"
یعنی اون کارت ویزیت کاملاً سفیده و هیچ آدرسی روش نیست. پوچه! این رفرنس به هیچ آبجکتی در حافظه اشاره نمیکنه.
class Point { public int X, Y; }
// اینجا یه رفرنس به اسم p داریم که به هیچجا اشاره نمیکنه
Point p = null;
Console.WriteLine(p == null);
// True💥 فاجعه کجا رخ میده؟ (NullReferenceException)
حالا اگه شما سعی کنید با اون کارت ویزیت سفید، درِ خونه رو باز کنید (یعنی به یکی از اعضای اون آبجکت مثل X دسترسی پیدا کنید)، برنامه قاطی میکنه و با تمام قدرت فریاد میزنه: NullReferenceException!
ترجمه این خطا به زبون خودمونی اینه: "داری سعی میکنی از چیزی استفاده کنی که اصلاً وجود نداره!"
// این خط فاجعه رو به بار میاره!
// چون p به هیچ آبجکتی اشاره نمیکنه، X هم وجود نداره
Console.WriteLine(p.X);
🚫 تکلیف Value Type ها چیه؟
خبر خوب اینه که Value Type ها (مثل int, struct, bool) ذاتاً نمیتونن null باشن. اونا همیشه یه مقداری دارن (حتی اگه مقدار پیشفرضشون صفر باشه).
Value Type مثل یه سکه هست که یا این روئه یا اون رو؛ حالت "هیچی" نداره.
struct Point { public int X, Y; }
// این کدها اصلاً کامپایل نمیشن! چون Value Type نمیتونه null باشه
Point p = null; // Compile-time error
int x = null; // Compile-time errorنکته: البته C# برای شرایط خاص، چیزی به اسم "Nullable Value Types" داره (مثلاً int?) که بعداً بهش میرسیم. ولی اصل داستان همینه.
🤔 حرف حساب و تکنیک شما چیه؟
مقابله با null یه مهارت روزمرهست. از چک کردنهای ساده مثل if (myVar != null) گرفته تا تکنیکهای مدرنتر در C#.
استراتژی شما برای جلوگیری از این خطای رو مخ چیه؟ آیا از Null-conditional operator (?.) و Null-coalescing operator (??) زیاد استفاده میکنید؟ یا روش خاصی برای مدیریت null تو کدهاتون دارید؟
🔖 هشتگها :
#CSharp
#ErrorHandling #NullReferenceException
خانواده بزرگ اعداد در #C :
وقتی تو #C میخوایم با اعداد کار کنیم، فقط int رو نداریم! یه خانواده بزرگ و متنوع از انواع عددی وجود داره که هر کدوم برای یه کار خاص ساخته شدن و قدرتهای متفاوتی دارن.
بیاید با همه اعضای اصلی این خانواده آشنا بشیم تا بدونید برای هر موقعیتی، کدوم سرباز رو باید به میدان بفرستید.
اینها برای کار با اعداد کامل و بدون اعشار هستن.
انتخاب اول و پیشفرض برای ۹۰٪ کارها. برای شمارندهها، شناسهها (ID) و محاسبات عمومی، همیشه انتخاب اول شما int هست.
وقتی int برای جا دادن عددتون کم میاره، long مثل یه تانک وارد میشه و مشکل رو حل میکنه.
اینا نسخههای کوچیکتر int هستن. معمولاً بهشون نیاز پیدا نمیکنید، مگر اینکه روی حافظه خیلی حساس باشید.
اینا همون اعداد صحیح هستن، با این تفاوت که فقط مقادیر مثبت رو قبول میکنن (Unsigned). در عوض، میتونن اعداد مثبت بزرگتری رو نسبت به همتای علامتدار خودشون ذخیره کنن.
اینها برای کار با اعداد اعشاری و کسری به کار میان.
انتخاب پیشفرض و سریع برای اعداد اعشاریه. برای محاسبات علمی، گرافیکی و فیزیکی که سرعت در اونها مهمه، double بهترین گزینهست.
این یکی فوقالعاده دقیقه و هیچ خطای گردکردنی نداره. برای هر نوع محاسبه مالی و پولی، استفاده از هر چیزی به جز decimal یه اشتباه بزرگه!
مثل یه نسخه کوچیکتر و کمدقتتر از double میمونه. جایی کاربرد داره که هم به اعشار نیاز دارید و هم حافظه براتون خیلی مهمه.
وقتی تو #C میخوایم با اعداد کار کنیم، فقط int رو نداریم! یه خانواده بزرگ و متنوع از انواع عددی وجود داره که هر کدوم برای یه کار خاص ساخته شدن و قدرتهای متفاوتی دارن.
بیاید با همه اعضای اصلی این خانواده آشنا بشیم تا بدونید برای هر موقعیتی، کدوم سرباز رو باید به میدان بفرستید.
اعداد صحیح (Integers):
اینها برای کار با اعداد کامل و بدون اعشار هستن.
int
انتخاب اول و پیشفرض برای ۹۰٪ کارها. برای شمارندهها، شناسهها (ID) و محاسبات عمومی، همیشه انتخاب اول شما int هست.
long
وقتی int برای جا دادن عددتون کم میاره، long مثل یه تانک وارد میشه و مشکل رو حل میکنه.
short و sbyte
اینا نسخههای کوچیکتر int هستن. معمولاً بهشون نیاز پیدا نمیکنید، مگر اینکه روی حافظه خیلی حساس باشید.
uint, ulong, ushort, byte
اینا همون اعداد صحیح هستن، با این تفاوت که فقط مقادیر مثبت رو قبول میکنن (Unsigned). در عوض، میتونن اعداد مثبت بزرگتری رو نسبت به همتای علامتدار خودشون ذخیره کنن.
Real Numbers
اینها برای کار با اعداد اعشاری و کسری به کار میان.
double
انتخاب پیشفرض و سریع برای اعداد اعشاریه. برای محاسبات علمی، گرافیکی و فیزیکی که سرعت در اونها مهمه، double بهترین گزینهست.
decimal
این یکی فوقالعاده دقیقه و هیچ خطای گردکردنی نداره. برای هر نوع محاسبه مالی و پولی، استفاده از هر چیزی به جز decimal یه اشتباه بزرگه!
float
مثل یه نسخه کوچیکتر و کمدقتتر از double میمونه. جایی کاربرد داره که هم به اعشار نیاز دارید و هم حافظه براتون خیلی مهمه.
🪄 جادوی اعداد در #C :
نوشتن اعداد مثل یک حرفهای (Literals, Suffixes & More)
فکر میکنی نوشتن 1000 تو سیشارپ همیشه به معنی int هست؟ یا چطور به کامپایلر بفهمونیم منظورمون یه عدد decimalئه نه double؟
اینا جزئیات و "فوت کوزهگری" هستن که فرق کدنویس معمولی و حرفهای رو مشخص میکنه. بزن بریم این ترفندها رو یاد بگیریم!
👓 خوانایی کد با لیترالهای حرفهای
نوشتن اعداد، فقط مقدارشون نیست؛ خوانایی هم مهمه!
جداکننده ارقام (_): برای اعداد بزرگ، میتونید از آندرلاین برای جدا کردن ارقام و خوانایی بیشتر استفاده کنید. کامپایلر این آندرلاینها رو نادیده میگیره.
مبنای شانزده (Hexadecimal): با پیشوند 0x، میتونید اعداد رو در مبنای ۱۶ بنویسید. این برای کار با رنگها، فلگها و عملیات سطح پایین خیلی کاربردیه.
مبنای دو (Binary): با پیشوند 0b، میتونید مستقیماً عدد رو به صورت باینری وارد کنید.
🏷 پسوندها (Suffixes): تعیین تکلیف برای کامپایلر!
بعضی وقتا کامپایلر خودش نوع عدد رو حدس میزنه (که بهش میگیم Type Inference)، ولی گاهی ما باید حرف آخر رو بزنیم! اینجاست که پسوندها وارد میشن.
مهمترینهاشون اینان:
اگه این پسوند رو نذارید، کامپایلر عدد اعشاری شما رو double در نظر میگیره و کدتون خطا میده چون double به راحتی به float تبدیل نمیشه.
این یکی فوقالعاده مهمه. برای تمام محاسبات مالی و پولی، باید از decimal استفاده کنید و برای مقداردهی بهش، حتماً از پسوند M استفاده کنید.
این پسوندها کمتر ضروری هستن، چون معمولاً کامپایلر میتونه نوعشون رو حدس بزنه یا به صورت خودکار تبدیل کنه. ولی خوبه که بدونید وجود دارن.
❓ شما چطور اعداد رو مینویسید؟
اینا فقط یه سری قاعده خشک و خالی نیستن، ابزارهایی برای نوشتن کد دقیقتر و خواناتر هستن.
نوشتن اعداد مثل یک حرفهای (Literals, Suffixes & More)
فکر میکنی نوشتن 1000 تو سیشارپ همیشه به معنی int هست؟ یا چطور به کامپایلر بفهمونیم منظورمون یه عدد decimalئه نه double؟
اینا جزئیات و "فوت کوزهگری" هستن که فرق کدنویس معمولی و حرفهای رو مشخص میکنه. بزن بریم این ترفندها رو یاد بگیریم!
👓 خوانایی کد با لیترالهای حرفهای
نوشتن اعداد، فقط مقدارشون نیست؛ خوانایی هم مهمه!
جداکننده ارقام (_): برای اعداد بزرگ، میتونید از آندرلاین برای جدا کردن ارقام و خوانایی بیشتر استفاده کنید. کامپایلر این آندرلاینها رو نادیده میگیره.
int bigNumber = 1_000_000; // خیلی خواناتر از 1000000
var binaryData = 0b1010_1011_1100;
مبنای شانزده (Hexadecimal): با پیشوند 0x، میتونید اعداد رو در مبنای ۱۶ بنویسید. این برای کار با رنگها، فلگها و عملیات سطح پایین خیلی کاربردیه.
int redColor = 0xFF0000;
مبنای دو (Binary): با پیشوند 0b، میتونید مستقیماً عدد رو به صورت باینری وارد کنید.
var filePermissions = 0b110_110_100; // rwx-wx--
🏷 پسوندها (Suffixes): تعیین تکلیف برای کامپایلر!
بعضی وقتا کامپایلر خودش نوع عدد رو حدس میزنه (که بهش میگیم Type Inference)، ولی گاهی ما باید حرف آخر رو بزنیم! اینجاست که پسوندها وارد میشن.
مهمترینهاشون اینان:
F برای float
اگه این پسوند رو نذارید، کامپایلر عدد اعشاری شما رو double در نظر میگیره و کدتون خطا میده چون double به راحتی به float تبدیل نمیشه.
// float f = 4.5; // ❌ کامپایل نمیشه!
float f = 4.5F; // ✅ درسته!
M برای decimal
این یکی فوقالعاده مهمه. برای تمام محاسبات مالی و پولی، باید از decimal استفاده کنید و برای مقداردهی بهش، حتماً از پسوند M استفاده کنید.
// decimal balance = 100.99; // ❌ کامپایل نمیشه!
decimal balance = 100.99M; // ✅ درسته!
L برای long و U برای unsigned
این پسوندها کمتر ضروری هستن، چون معمولاً کامپایلر میتونه نوعشون رو حدس بزنه یا به صورت خودکار تبدیل کنه. ولی خوبه که بدونید وجود دارن.
long bigNum = 9_000_000_000L; // با L بهش میگیم این یه عدد خیلی بزرگه
❓ شما چطور اعداد رو مینویسید؟
اینا فقط یه سری قاعده خشک و خالی نیستن، ابزارهایی برای نوشتن کد دقیقتر و خواناتر هستن.
🔖 هشتگها :
#CSharp
#CodingTips
#Literals
🎲 قوانین بازی با اعداد در #C :
تا حالا فکر کردید وقتی یه int رو توی یه long میریزید، یا یه double رو به زور تو یه int جا میدید، پشت صحنه چه اتفاقی میفته؟
این تبدیلها (Conversions) قوانین خودشون رو دارن و ندونستن این قوانین، میتونه به از دست رفتن داده و باگهای وحشتناک ختم بشه. بزن بریم این قوانین رو یک بار برای همیشه یاد بگیریم.
این تبدیلها مثل ریختن آب از یه لیوان کوچیک تو یه سطل بزرگه؛ کاملاً امن، بدون دردسر و بدون از دست رفتن حتی یک قطره آب!
وقتی نوع داده مقصد، گنجایش تمام مقادیر ممکن در نوع داده مبدأ رو داشته باشه، #C خودش هوشمندانه و به صورت خودکار این تبدیل رو انجام میده.
اینجا دیگه سیشارپ به شما اجازه نمیده این کار رو خودکار انجام بدید. شما باید با مسئولیت خودتون و با استفاده از Casting (قرار دادن نوع مقصد داخل پرانتز)، به کامپایلر بگید: "میدونم دارم چیکار میکنم و ریسکش رو میپذیرم!"
💣 دو تله مرگبار در تبدیلهای صریح
وقتی کست میکنید، حواستون به این دوتا تله باشه:
بریده شدن اعشار (Truncation):
وقتی یه عدد اعشاری (double یا float) رو به یه عدد صحیح (int) کست میکنید، بخش اعشاری عدد گرد نمیشه، بلکه وحشیانه بریده و حذف میشه!
از دست رفتن دقت (Precision Loss):
گاهی اوقات، حتی در تبدیلهای امن (مثل int به float)، ممکنه دقت رو از دست بدید. float میتونه اعداد خیلی بزرگتری رو نشون بده، ولی دقتش در جزئیات کمتر از int هست.
همه چیز درباره تبدیلهای عددی (Casting)
تا حالا فکر کردید وقتی یه int رو توی یه long میریزید، یا یه double رو به زور تو یه int جا میدید، پشت صحنه چه اتفاقی میفته؟
این تبدیلها (Conversions) قوانین خودشون رو دارن و ندونستن این قوانین، میتونه به از دست رفتن داده و باگهای وحشتناک ختم بشه. بزن بریم این قوانین رو یک بار برای همیشه یاد بگیریم.
✅ نوع اول: تبدیلهای امن و ضمنی (Implicit)
این تبدیلها مثل ریختن آب از یه لیوان کوچیک تو یه سطل بزرگه؛ کاملاً امن، بدون دردسر و بدون از دست رفتن حتی یک قطره آب!
وقتی نوع داده مقصد، گنجایش تمام مقادیر ممکن در نوع داده مبدأ رو داشته باشه، #C خودش هوشمندانه و به صورت خودکار این تبدیل رو انجام میده.
int myInt = 12345;
// int (32-bit) به راحتی در long (64-bit) جا میشه
long myLong = myInt;
// int (عدد صحیح) به راحتی به double (عدد اعشاری) تبدیل میشه
double myDouble = myInt;
float myFloat = 123.45F;
// float (32-bit) به راحتی در double (64-bit) جا میشه
double anotherDouble = myFloat;
⚠️ نوع دوم: تبدیلهای خطرناک و صریح (Explicit Casting)حالا برعکسش رو تصور کنید: میخواید آب یه سطل بزرگ رو تو یه لیوان کوچیک جا بدید. احتمال اینکه آب سرریز بشه و بخشی ازش از دست بره، خیلی زیاده!
اینجا دیگه سیشارپ به شما اجازه نمیده این کار رو خودکار انجام بدید. شما باید با مسئولیت خودتون و با استفاده از Casting (قرار دادن نوع مقصد داخل پرانتز)، به کامپایلر بگید: "میدونم دارم چیکار میکنم و ریسکش رو میپذیرم!"
long bigNum = 3_000_000_000L;
double myDouble = 12345.6789;
// برای جا دادن long در int، باید کست کنیم
// اگه عدد بزرگ باشه، داده از دست میره!
int myInt = (int)bigNum;
// برای جا دادن double در int، باید کست کنیم
int anotherInt = (int)myDouble; // مقدارش میشه 12345
💣 دو تله مرگبار در تبدیلهای صریح
وقتی کست میکنید، حواستون به این دوتا تله باشه:
بریده شدن اعشار (Truncation):
وقتی یه عدد اعشاری (double یا float) رو به یه عدد صحیح (int) کست میکنید، بخش اعشاری عدد گرد نمیشه، بلکه وحشیانه بریده و حذف میشه!
double price = 99.99;
int priceAsInt = (int)price; // مقدارش میشه 99، نه 100!
از دست رفتن دقت (Precision Loss):
گاهی اوقات، حتی در تبدیلهای امن (مثل int به float)، ممکنه دقت رو از دست بدید. float میتونه اعداد خیلی بزرگتری رو نشون بده، ولی دقتش در جزئیات کمتر از int هست.
int bigInt = 100_000_001;
// اینجا مقدار حفظ میشه، ولی دقت از بین میره
float myFloat = bigInt;
// وقتی برمیگردونیم، میبینیم که عدد تغییر کرده!
int convertedInt = (int)myFloat; // مقدارش میشه 100_000_000
🔖 هشتگها :
#CSharp
#CodingTips
#Casting
💡 نکته سریع #C :
همه ما با عملگرهای حسابی مثل + و * آشناییم. ولی آیا میدونستید یه نکتهی جالب و مهم در موردشون موقع کار با تایپهای کوچیکی مثل byte و short وجود داره؟
فرض کنید میخواید دو تا byte رو با هم جمع کنید. چه اتفاقی میفته؟
چرا خطا میده؟
چون در #C، وقتی عملیات حسابی روی تایپهای کوچکتر از int (مثل byte یا short) انجام میشه، اونها اول به صورت خودکار به int تبدیل میشن و حاصل عملیات هم یک int خواهد بود!
بنابراین، کد صحیح این شکلیه:
دلیل این کار چیه؟
این رفتار برای بهینهسازی و سادگی در سطح پردازنده و CLR طراحی شده. کار کردن با int (که معمولاً اندازه کلمه پردازنده است) سریعتر و بهینهتره.
حالا اگه حتماً نتیجه رو تو یه byte میخواید چی؟
باید خودتون صراحتاً نتیجه رو کست کنید (و البته حواستون به سرریز شدن یا Overflow باشه!):
حرف آخر: یادتون باشه، حاصل عملیات حسابی روی تایپهای کوچیک، همیشه یه intئه! دونستن همین نکته کوچیک، جلوی خیلی از خطاهای کامپایل ناگهانی رو میگیره.
راز عملگرهای حسابی با تایپهای کوچک!
همه ما با عملگرهای حسابی مثل + و * آشناییم. ولی آیا میدونستید یه نکتهی جالب و مهم در موردشون موقع کار با تایپهای کوچیکی مثل byte و short وجود داره؟
فرض کنید میخواید دو تا byte رو با هم جمع کنید. چه اتفاقی میفته؟
byte b1 = 10;
byte b2 = 20;
// انتظار داریم این کد کار کنه، ولی...
// ❌ این خط کامپایل نمیشه!
byte result = b1 + b2;
چرا خطا میده؟
چون در #C، وقتی عملیات حسابی روی تایپهای کوچکتر از int (مثل byte یا short) انجام میشه، اونها اول به صورت خودکار به int تبدیل میشن و حاصل عملیات هم یک int خواهد بود!
بنابراین، کد صحیح این شکلیه:
// ✅ درسته! حاصل جمع دو بایت، یک int است.
int result = b1 + b2;
دلیل این کار چیه؟
این رفتار برای بهینهسازی و سادگی در سطح پردازنده و CLR طراحی شده. کار کردن با int (که معمولاً اندازه کلمه پردازنده است) سریعتر و بهینهتره.
حالا اگه حتماً نتیجه رو تو یه byte میخواید چی؟
باید خودتون صراحتاً نتیجه رو کست کنید (و البته حواستون به سرریز شدن یا Overflow باشه!):
// ✅ درسته (با مسئولیت خودتان!)
byte result = (byte)(b1 + b2);
حرف آخر: یادتون باشه، حاصل عملیات حسابی روی تایپهای کوچیک، همیشه یه intئه! دونستن همین نکته کوچیک، جلوی خیلی از خطاهای کامپایل ناگهانی رو میگیره.
🔖 هشتگها :
#CSharp
#QuickTip