2️⃣ اصل دوم SOLID: باز/بسته (Open/Closed Principle)
تا حالا شده یه تیکه کد بنویسید که خوب کار میکنه، ولی هر بار که یه قابلیت جدید میخواید، مجبور میشید برگردید و همون کد قدیمی و حساس رو دستکاری کنید؟ این کار ریسک ایجاد باگهای جدید رو خیلی بالا میبره.
اصل باز/بسته (OCP) برای حل همین مشکل اومده.
این اصل چی میگه؟ 🎯
به زبان ساده:
کلاسها و ماژولهای شما باید برای "توسعه" (Extension) باز، ولی برای "تغییر" (Modification) بسته باشند.
یعنی شما باید بتونید رفتار جدیدی به سیستم اضافه کنید، بدون اینکه نیاز به تغییر کدهای موجودی که قبلاً تست شده و کار میکنه، داشته باشید.
مثال عملی: کد "شکننده" 👎 در برابر کد "انعطافپذیر" 👍
فرض کنید یه کلاسی داریم که مساحت اشکال مختلف رو محاسبه میکنه.
مثال بد (نقض اصل باز/بسته):
این کد برای تغییر "بسته" نیست. اگه بخوایم یه شکل جدید مثل "مثلث" رو اضافه کنیم، مجبوریم این کلاس رو باز کنیم و یه if جدید بهش اضافه کنیم. این یعنی دستکاری کد قدیمی و ریسک ایجاد باگ.
public class AreaCalculator
{
public double CalculateTotalArea(object[] shapes)
{
double totalArea = 0;
foreach (var shape in shapes)
{
if (shape is Rectangle r)
{
totalArea += r.Width * r.Height;
}
if (shape is Circle c)
{
totalArea += Math.PI * c.Radius * c.Radius;
}
// برای اضافه کردن مثلث، باید این کلاس رو دستکاری کنیم!
}
return totalArea;
}
}
مثال خوب (رعایت اصل باز/بسته):
حالا با استفاده از یک انتزاع (Abstraction) مثل interface، کد رو جوری بازنویسی میکنیم که برای توسعه "باز" باشه.
قدم اول: ساختن یک قرارداد (Interface)
public interface IShape
{
double CalculateArea();
}
قدم دوم: پیادهسازی قرارداد برای هر شکل
public class Rectangle : IShape
{
public double Width { get; set; }
public double Height { get; set; }
public double CalculateArea() => Width * Height;
}
public class Circle : IShape
{
public double Radius { get; set; }
public double CalculateArea() => Math.PI * Radius * Radius;
}
قدم سوم: کلاس محاسبهگر انعطافپذیر
حالا کلاس AreaCalculator ما دیگه به نوع مشخصی وابسته نیست، فقط با قرارداد IShape کار میکنه.
public class AreaCalculator
{
public double CalculateTotalArea(IShape[] shapes)
{
double totalArea = 0;
foreach (var shape in shapes)
{
totalArea += shape.CalculateArea();
}
return totalArea;
}
}
جادو اینجا اتفاق میفته: حالا اگه بخوایم شکل جدید "مثلث" رو اضافه کنیم، هیچ نیازی به تغییر کلاس AreaCalculator نداریم! فقط یه کلاس جدید میسازیم:
public class Triangle : IShape
{
public double Base { get; set; }
public double Height { get; set; }
public double CalculateArea() => 0.5 * Base * Height;
}
کد ما برای توسعه (اضافه کردن کلاس جدید) باز بود، ولی برای تغییر (دستکاری AreaCalculator) بسته بود!
🔖 هشتگها:
#CSharp #Programming #Developer #SOLID #SoftwareArchitecture #CleanCode #OCP
3️⃣ اصل سوم SOLID: جایگزینی لیسکوف (Liskov Substitution Principle)
تا حالا شده یه کلاس فرزند بسازید که از یه کلاس پدر ارثبری میکنه، ولی وقتی ازش به جای پدر استفاده میکنید، یهو همه چی به هم میریزه و برنامه رفتار غیرمنتظرهای نشون میده؟
اصل جایگزینی لیسکوف (LSP) دقیقاً برای جلوگیری از همین فاجعه طراحی شده.
این اصل چی میگه؟ 🎯
به زبان ساده:
شما باید همیشه بتونید یک نمونه از کلاس فرزند (Subclass) رو به جای یک نمونه از کلاس پدر (Superclass) استفاده کنید، بدون اینکه برنامه به مشکل بخوره یا رفتارش عوض بشه.یعنی کلاس فرزند نباید "قراردادها" و انتظاراتی که از کلاس پدر میره رو زیر پا بذاره. اگه کلاس پدر قول داده یه کاری رو انجام بده، کلاس فرزند هم باید همون قول رو بده، نه اینکه بزنه زیرش!
مثال عملی: کابوس معروف مربع و مستطیل!
این یه مثال کلاسیکه که نقض LSP رو به بهترین شکل نشون میده.
مثال بد (نقض اصل جایگزینی لیسکوف):
در نگاه اول، به نظر منطقی میاد که Square (مربع) از Rectangle (مستطیل) ارثبری کنه، چون مربع یه نوع خاص از مستطیله. ولی این کار، قرارداد رو میشکنه!
public class Rectangle
{
public virtual int Width { get; set; }
public virtual int Height { get; set; }
}
public class Square : Rectangle
{
// برای اینکه مربع باقی بمونه، وقتی عرض رو تغییر میدیم،
// باید ارتفاع رو هم تغییر بدیم (و برعکس).
public override int Width
{
set { base.Width = base.Height = value; }
}
public override int Height
{
set { base.Width = base.Height = value; }
}
}
مشکل کجاست؟
حالا یه متدی رو تصور کنید که یه Rectangle میگیره و انتظار داره با تغییر عرض، ارتفاع ثابت بمونه. اگه ما بهش یه Square پاس بدیم، کل منطقش به هم میریزه!
public void SomeMethod(Rectangle r)
{
// این متد انتظار داره با تغییر عرض، ارتفاع ثابت بمونه
r.Width = 10;
r.Height = 5;
// اگه r یک مستطیل واقعی باشه، مساحت 50 میشه
// ولی اگه یه مربع بهش پاس داده باشیم، مساحت 25 میشه! (چون ارتفاع هم 5 شده)
// این یعنی رفتار برنامه غیرمنتظره شده!
Console.WriteLine(r.Width * r.Height);
}
اینجا کلاس فرزند (Square) نتونست بدون دردسر جای پدرش (Rectangle) رو بگیره.
مثال خوب (رعایت اصل جایگزینی لیسکوف):
راه حل اینه که به جای وراثت مستقیم، از یه انتزاع مشترک مثل interface استفاده کنیم.
public interface IShape
{
double CalculateArea();
}
public class Rectangle : IShape
{
public int Width { get; set; }
public int Height { get; set; }
public double CalculateArea() => Width * Height;
}
public class Square : IShape
{
public int Side { get; set; }
public double CalculateArea() => Side * Side;
}
حالا دیگه هیچکدوم قرارداد اون یکی رو نمیشکنه و هر کدوم زندگی خودشون رو دارن.
🔖 هشتگها:
#CSharp #Programming #Developer #SOLID #SoftwareArchitecture #CleanCode #LSP
4️⃣ اصل چهارم SOLID: تفکیک اینترفیسها (Interface Segregation Principle)
تا حالا شده یه اینترفیس (Interface) رو پیادهسازی کنید و ببینید مجبورید کلی متد رو به صورت خالی یا با پرتاب Exception پیادهسازی کنید، چون اصلاً به کار کلاس شما نمیان؟
این مشکل معمولاً از طراحی اینترفیسهای بزرگ و "چاق" (Fat Interfaces) به وجود میاد. اصل تفکیک اینترفیسها (ISP) برای حل همین مشکل طراحی شده.
این اصل چی میگه؟ 🎯
به زبان ساده:
کلاسها نباید مجبور بشن اینترفیسهایی رو پیادهسازی کنن که به متدهای اون نیازی ندارن.
به عبارت دیگه، به جای ساختن یک اینترفیس بزرگ و همهکاره، بهتره چندین اینترفیس کوچک، تخصصی و مجزا بسازیم.
مثال عملی: کابوس پرینترهای همهکاره!
فرض کنید یه اینترفیس برای کار با دستگاههای اداری طراحی میکنیم.
مثال بد (نقض اصل تفکیک اینترفیسها):
اینجا یه اینترفیس "چاق" داریم. اگه یه پرینتر ساده و ارزون داشته باشیم که فقط قابلیت پرینت داره، باز هم مجبوره متدهای Scan و Fax رو پیادهسازی کنه، که این کار منطقی نیست و کد رو کثیف میکنه.
// ❌ اینترفیس "چاق" و بد
public interface IMultiFunctionDevice
{
void Print(string content);
void Scan(string content);
void Fax(string content);
}
public class CheapPrinter : IMultiFunctionDevice
{
public void Print(string content)
{
// OK
}
public void Scan(string content)
{
// این پرینتر اسکنر نداره! مجبوریم خطا برگردونیم
throw new NotImplementedException();
}
public void Fax(string content)
{
// این پرینتر فکس هم نداره!
throw new NotImplementedException();
}
}
مثال خوب (رعایت اصل تفکیک اینترفیسها):
حالا میایم و اون اینترفیس بزرگ رو به چند اینترفیس کوچکتر و تخصصیتر میشکنیم.
// ✅ اینترفیسهای کوچک و تخصصی
public interface IPrinter
{
void Print(string content);
}
public interface IScanner
{
void Scan(string content);
}
حالا هر کلاسی، فقط اینترفیسی رو پیادهسازی میکنه که واقعاً بهش نیاز داره.
// این پرینتر ساده، فقط قرارداد پرینت رو امضا میکنه
public class CheapPrinter : IPrinter
{
public void Print(string content)
{
// OK
}
}
// این دستگاه همهکاره، هر دو قرارداد رو امضا میکنه
public class AllInOnePrinter : IPrinter, IScanner
{
public void Print(string content)
{
// OK
}
public void Scan(string content)
{
// OK
}
}
کد ما حالا خیلی تمیزتر، انعطافپذیرتر و قابل فهمتره!
🔖 هشتگها:
#CSharp #Programming #Developer #SOLID #SoftwareArchitecture #CleanCode #ISP
5️⃣ اصل پنجم SOLID: وارونگی وابستگی (Dependency Inversion Principle)
تا حالا شده یه کلاس بنویسید که داخلش یه کلاس دیگه رو با new میسازید و بعداً برای تغییر اون کلاس داخلی، مجبور میشید کلاس اصلی رو هم دستکاری کنید؟ این یعنی کدهای شما به هم سفت و سخت (Tightly Coupled) وصل شدن.
اصل وارونگی وابستگی (DIP) برای حل همین مشکل و ایجاد کدهای انعطافپذیر طراحی شده.
این اصل چی میگه؟ 🎯
این اصل دو بخش مهم داره:
ماژولهای سطح بالا نباید به ماژولهای سطح پایین وابسته باشند. هر دو باید به انتزاع (Abstraction) وابسته باشند.به زبان ساده: کلاسهای اصلی و سطح بالای شما (مثلاً بیزنس لاجیک) نباید به جزئیات پیادهسازی (مثلاً نحوه کار با دیتابیس یا ارسال ایمیل) وابسته باشن. در عوض، هر دو باید به یک قرارداد مشترک (Interface) وابسته باشن.
انتزاعها نباید به جزئیات وابسته باشند. این جزئیات هستن که باید به انتزاعها وابسته باشند.
مثال عملی: سیستم اطلاعرسانی
فرض کنید یه سیستمی برای اطلاعرسانی به کاربر داریم.
مثال بد (نقض اصل وارونگی وابستگی):
اینجا کلاس سطح بالای Notification مستقیماً به کلاس سطح پایین EmailSender وابسته است. اگه فردا بخوایم به جای ایمیل، با SMS اطلاعرسانی کنیم، مجبوریم کلاس Notification رو تغییر بدیم. این یعنی وابستگی سفت و سخت.
// ❌ این کلاس سطح پایین است
public class EmailSender
{
public void Send() => Console.WriteLine("Email sent!");
}
// ❌ این کلاس سطح بالاست و مستقیماً به کلاس پایینی وابسته است
public class Notification
{
private readonly EmailSender _emailSender = new EmailSender();
public void SendNotification()
{
_emailSender.Send();
}
}
مثال خوب (رعایت اصل وارونگی وابستگی):
حالا با استفاده از یک Interface، این وابستگی رو "وارونه" میکنیم.
قدم اول: ساختن قرارداد (Interface)
این قرارداد در لایه سطح بالا تعریف میشه.
public interface IMessageSender
{
void SendMessage();
}
قدم دوم: کلاسهای سطح پایین از قرارداد پیروی میکنند
public class EmailSender : IMessageSender
{
public void SendMessage() => Console.WriteLine("Email sent!");
}
public class SmsSender : IMessageSender
{
public void SendMessage() => Console.WriteLine("SMS sent!");
}
قدم سوم: کلاس سطح بالا به قرارداد وابسته است، نه به جزئیات
public class Notification
{
private readonly IMessageSender _sender;
// وابستگی از طریق Constructor تزریق میشود (Dependency Injection)
public Notification(IMessageSender sender)
{
_sender = sender;
}
public void SendNotification()
{
_sender.SendMessage();
}
}
جادو اینجا اتفاق میفته: حالا کلاس Notification دیگه کاری نداره که پیام چطوری ارسال میشه (با ایمیل یا SMS). اون فقط "قرارداد" رو میشناسه. ما میتونیم موقع ساختن آبجکت Notification، هر نوع IMessageSender که دوست داریم رو بهش پاس بدیم، بدون اینکه یک کلمه از کدش رو تغییر بدیم!
🤔 حرف حساب و تجربه شما
این DIP قلب معماریهای تمیز و مدرنه و اساس کار تزریق وابستگی (Dependency Injection) هست. رعایت این اصل، کد شما رو فوقالعاده انعطافپذیر، قابل تست و توسعهپذیر میکنه.
شما چقدر به این اصل در پروژههاتون اهمیت میدید؟ بهترین مثالی که از کاربرد این اصل تو ذهنتون دارید چیه؟
💬 بحث و گفتگوی بیشتر در گروه کامیونیتی:
[C# Geeks Community]
🔖 هشتگها:
#CSharp #Programming #Developer #SOLID #SoftwareArchitecture #CleanCode #DIP
🛡 دستور using در #C: بهترین دوست IDisposable و راهی برای جلوگیری از نشت منابعوقتی با فایلها، دیتابیس، ارتباطات شبکه یا هر منبع خارجی دیگهای کار میکنید، یه خطر بزرگ وجود داره: یادتون بره منابعی که باز کردید رو ببندید! این کار باعث "نشت منابع" (Resource Leaks) و مشکلات جدی در پرفورمنس و پایداری برنامه میشه.
سیشارپ یه راه حل خیلی شیک، امن و حرفهای برای این مشکل داره: دستور using.
1️⃣ مشکل کجاست؟ IDisposable و مدیریت منابع
بعضی از آبجکتها در داتنت، منابعی رو مدیریت میکنن که Garbage Collector به صورت خودکار نمیتونه اونها رو آزاد کنه (مثل دستگیره فایل در سیستمعامل). این کلاسها، اینترفیس IDisposable رو پیادهسازی میکنن که فقط یک متد به اسم Dispose() داره.
وظیفه ما به عنوان برنامهنویس اینه که همیشه و تحت هر شرایطی، بعد از تموم شدن کارمون با این آبجکتها، متد Dispose() اونها رو صدا بزنیم.
2️⃣ راه حل قدیمی (و زشت): try...finally 👎
راه سنتی برای اینکه مطمئن بشیم Dispose() همیشه صدا زده میشه (حتی اگه وسط کار یه Exception رخ بده)، استفاده از بلوک try...finally هست:
StreamReader reader = null;
try
{
reader = new StreamReader("myFile.txt");
// ... کار کردن با فایل ...
}
finally
{
if (reader != null)
{
reader.Dispose(); // تضمین میکنه که فایل همیشه بسته میشه
}
}
این کد کار میکنه، ولی طولانی و زشته.
3️⃣ راه حل مدرن و شیک: دستور using ✨
دستور using یه "شکر سینتکسی" (Syntactic Sugar) برای همون بلوک try...finally هست.
به محض اینکه اجرای کد از بلوک using خارج بشه (چه به صورت عادی و چه به خاطر یه Exception)، کامپایلر به صورت خودکار متد Dispose() رو روی اون آبجکت صدا میزنه. اینجوری کد شما خیلی تمیزتر، کوتاهتر و امنتر میشه.
// این کد دقیقاً معادل کد try...finally بالاست
using (StreamReader reader = new StreamReader("myFile.txt"))
{
// ... کار کردن با فایل ...
}
// reader.Dispose() اینجا به صورت خودکار در هر حالتی صدا زده میشه
4️⃣ فرم جدیدتر (از C# 8): using declarations 🚀
از C# 8 به بعد، کار حتی از این هم سادهتر شده. اگه متغیر using رو به این شکل تعریف کنید، دیگه نیازی به آکولاد {} ندارید و اون متغیر در انتهای اسکوپ فعلی (معمولاً در انتهای متد) به صورت خودکار Dispose میشه.
void ReadFile()
{
using var reader = new StreamReader("myFile.txt");
using var writer = new StreamWriter("log.txt");
// ... کار کردن با فایلها ...
} // reader.Dispose() و writer.Dispose() اینجا به صورت خودکار صدا زده میشن
🤔 حرف حساب و قانون طلایی
قانون طلایی: هر کلاسی که IDisposable رو پیادهسازی کرده، باید داخل یه بلوک using (یا با سینتکس جدیدش) ازش استفاده بشه.
شما بیشتر از کدوم فرمت using استفاده میکنید؟ کلاسیک با براکت یا فرمت جدید بدون براکت؟
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #BestPractices #CleanCode #IDisposable
😵💫 برنامهنویس NET.؟
میخوای از باگهای دردناک، امنیت داغون و کد اسپاگتی در امان بمونی؟
این 10 قاتل خاموش رو دستکم نگیر!
(حتی تو پروژههای بهظاهر «Enterprise» هم میبینمشون…)
⸻
1️⃣ بدون اعتبارسنجی ورودی؟ خداحافظ با ثبات 😬
دادهی خراب بیاد تو، همهچی میریزه به هم.
پاک کردن دادهی خراب ۱۰ برابر سختتر از جلوگیری از ورودشه!
🧱 همیشه ورودی رو چک کن → Validate before trust
⸻
2️⃣ مقادیر هاردکد شده 🎯
عاشق سرتاسری Replace زدن تو پروژهای؟
از «عدد جادویی» استفاده کن 😎
نه؟ پس برو سراغ:
🔸 فایل پیکربندی
🔸 ثابتها
🔸 دیتابیس
⸻
3️⃣ وابستگی سفت و سخت بین کلاسها 🔗
وقتی یه کلاس به کلاس دیگه دوخته شده باشه، انعطاف میره هوا.
🙅♂️ با abstraction (مثل Interface) کدت رو قابل تغییر نگه دار.
⸻
4️⃣ تست واحد؟ وقت ندارم؟ 😐
اگه الان وقت نداری تست بنویسی،
فردا کلی وقت داری باگهات رو دیباگ کنی! 🔥
⸻
5️⃣ مدیریت نکردن Exception ها 💣
اگه استکتریس کامل رو فرستادی تو پاسخ HTTP، یه کادو به هکر دادی!
📛 ارورها رو لاگ کن.
✅ پیام امن بده به کاربر.
⸻
6️⃣ کد غیرقابل خوندن 🤯
واسه انسان بنویس نه ماشین!
اگه باید یه کد رو ۳ بار بخونی تا بفهمی چی میگه، خیلی پیچیدهست.
🧼 تمیز بنویس تا خودت ۳ ماه دیگه هم بفهمیش!
⸻
7️⃣ طراحی ضعیف دیتابیس 🐢
طرح دیتای اشتباه یعنی:
🔹 کندی
🔹 غیرقابل توسعه بودن
🔹 زجر در آینده
بلندمدت فکر کن موقع مدلسازی!
⸻
8️⃣ امنیت؟ بذار بعداً! ❌
👾 اینجوری هک میشی رفیق!
✅ ورودیها رو بررسی کن
✅ کمترین سطح دسترسی
✅ هرچی میاد رو پاکسازی کن (Sanitize)
امنیت چیز اختیاری نیست… هیچوقت!
⸻
9️⃣ بدون لاگ و مانیتورینگ 🚫📊
برنامه رو منتشر کردی؟ دمت گرم!
ولی حالا از کجا میفهمی داره درست کار میکنه؟
🕵️♂️ بدون لاگ = پرواز کور
⸻
🔟 اختراع دوباره چرخ؟ 😅
مگه قراره ORM اختراع کنی؟
تا وقتی یه ابزار تستشده و امن هست،
✅ استفادهش کن
🧠 وقتتو صرف چیزای ارزشمندتر کن!
⸻
🎯 لازم نیست کدت بینقص باشه…
ولی اگه اینا رو نادیده بگیری،
کد Legacy درست کردی، نه پروژه حرفهای!
⸻
میخوای از باگهای دردناک، امنیت داغون و کد اسپاگتی در امان بمونی؟
این 10 قاتل خاموش رو دستکم نگیر!
(حتی تو پروژههای بهظاهر «Enterprise» هم میبینمشون…)
⸻
1️⃣ بدون اعتبارسنجی ورودی؟ خداحافظ با ثبات 😬
دادهی خراب بیاد تو، همهچی میریزه به هم.
پاک کردن دادهی خراب ۱۰ برابر سختتر از جلوگیری از ورودشه!
🧱 همیشه ورودی رو چک کن → Validate before trust
⸻
2️⃣ مقادیر هاردکد شده 🎯
عاشق سرتاسری Replace زدن تو پروژهای؟
از «عدد جادویی» استفاده کن 😎
نه؟ پس برو سراغ:
🔸 فایل پیکربندی
🔸 ثابتها
🔸 دیتابیس
⸻
3️⃣ وابستگی سفت و سخت بین کلاسها 🔗
وقتی یه کلاس به کلاس دیگه دوخته شده باشه، انعطاف میره هوا.
🙅♂️ با abstraction (مثل Interface) کدت رو قابل تغییر نگه دار.
⸻
4️⃣ تست واحد؟ وقت ندارم؟ 😐
اگه الان وقت نداری تست بنویسی،
فردا کلی وقت داری باگهات رو دیباگ کنی! 🔥
⸻
5️⃣ مدیریت نکردن Exception ها 💣
اگه استکتریس کامل رو فرستادی تو پاسخ HTTP، یه کادو به هکر دادی!
📛 ارورها رو لاگ کن.
✅ پیام امن بده به کاربر.
⸻
6️⃣ کد غیرقابل خوندن 🤯
واسه انسان بنویس نه ماشین!
اگه باید یه کد رو ۳ بار بخونی تا بفهمی چی میگه، خیلی پیچیدهست.
🧼 تمیز بنویس تا خودت ۳ ماه دیگه هم بفهمیش!
⸻
7️⃣ طراحی ضعیف دیتابیس 🐢
طرح دیتای اشتباه یعنی:
🔹 کندی
🔹 غیرقابل توسعه بودن
🔹 زجر در آینده
بلندمدت فکر کن موقع مدلسازی!
⸻
8️⃣ امنیت؟ بذار بعداً! ❌
👾 اینجوری هک میشی رفیق!
✅ ورودیها رو بررسی کن
✅ کمترین سطح دسترسی
✅ هرچی میاد رو پاکسازی کن (Sanitize)
امنیت چیز اختیاری نیست… هیچوقت!
⸻
9️⃣ بدون لاگ و مانیتورینگ 🚫📊
برنامه رو منتشر کردی؟ دمت گرم!
ولی حالا از کجا میفهمی داره درست کار میکنه؟
🕵️♂️ بدون لاگ = پرواز کور
⸻
🔟 اختراع دوباره چرخ؟ 😅
مگه قراره ORM اختراع کنی؟
تا وقتی یه ابزار تستشده و امن هست،
✅ استفادهش کن
🧠 وقتتو صرف چیزای ارزشمندتر کن!
⸻
🎯 لازم نیست کدت بینقص باشه…
ولی اگه اینا رو نادیده بگیری،
کد Legacy درست کردی، نه پروژه حرفهای!
⸻
🔖 هشتگها:
#DotNet #CSharp #CodingTips #BestPractices #Programming #SoftwareEngineering #DevLife
💾 حافظه کلاسها در #C: راهنمای کامل Fields, readonly و const
تا حالا شده از خودتون بپرسید فرق بین یه فیلد readonly و یه const چیه؟ یا اینکه متغیرهایی که داخل یه کلاس تعریف میکنیم (فیلدها) چطور و کِی مقداردهی میشن؟
امروز میخوایم بریم تو دل کلاسها و با اعضای دادهای اونها و قوانین حاکم بر اونها آشنا بشیم.
1️⃣ فیلدها (Fields):
حافظه داخلی یک آبجکت فیلدها، متغیرهایی هستن که داخل یه کلاس یا struct تعریف میشن و "وضعیت" یا "داده"های اون آبجکت رو در طول عمرش نگه میدارن. هر نمونه (instance) از کلاس، یک کپی مستقل از فیلدهای خودش رو داره.
💡نکته حرفهای (قرارداد نامگذاری):مقداردهی اولیه فیلدها:
یه قرارداد رایج و خوب، استفاده از آندرلاین (_) برای فیلدهای private هست تا به راحتی از متغیرهای محلی و پارامترها تشخیص داده بشن (مثلاً name_).
مقداردهی اولیه به فیلدها اختیاریه. اگه مقداری بهشون ندید، مقدار پیشفرض نوعشون رو میگیرن. نکته مهم اینه که این مقداردهی قبل از اجرای اولین خط کد در سازنده (constructor) انجام میشه.
2️⃣ کلید readonly: فیلدهای فقط-خواندنی 🔒
اگه میخواید یه فیلد فقط یک بار مقداردهی بشه و بعد از ساخته شدن آبجکت دیگه هرگز تغییر نکنه، از readonly استفاده کنید. این برای مقادیری که در زمان ساخت آبجکت مشخص میشن ولی بعدش باید ثابت بمونن، عالیه.
قانون مهم: فیلدهای readonly فقط در دو جا میتونن مقدار بگیرن: یا موقع تعریف اولیه، یا داخل سازنده (constructor) اون کلاس.
public class UserProfile
{
// مقداردهی موقع تعریف
public readonly Guid UserId = Guid.NewGuid();
// تعریف بدون مقدار اولیه
public readonly DateTime RegistrationDate;
public UserProfile()
{
// مقداردهی در سازنده
RegistrationDate = DateTime.UtcNow;
}
public void UpdateProfile()
{
// UserId = Guid.NewGuid(); // ❌ خطای زمان کامپایل!
}
}
3️⃣ دوئل بزرگ: const در برابر static readonly ⚔️
این یکی از کلاسیکترین سوالات مصاحبه و یکی از مهمترین نکات در طراحی حرفهایه. هر دو برای مقادیر ثابت به کار میرن، ولی تفاوتهای حیاتی دارن.
🔵 const (ثابت زمان کامپایل):
• مقدارش باید در زمان کامپایل مشخص و ثابت باشه
(const double Pi = 3.14;).
• کامپایلر مقدارش رو مستقیماً در کد جایگزین میکنه (مثل ماکرو).
• فقط برای انواع داده ساده (عددی، رشته، بولین و...) قابل استفادهست.
🔴 static readonly (ثابت زمان اجرا):
• مقدارش در زمان اجرا (معمولاً موقع استارت برنامه) مشخص میشه.
• یه فیلد واقعیه و در حافظه نگهداری میشه، مقدارش جایگزین نمیشه.
• برای هر نوع دادهای، حتی آبجکتهای پیچیده، قابل استفادهست.
public static readonly DateTime StartupTime = DateTime.Now;
😈تله خطرناک public const:
اگه شما یه public const رو در یک کتابخانه (Assembly A) تعریف کنید و در یک پروژه دیگه (Assembly B) ازش استفاده کنید، مقدار اون ثابت موقع کامپایل در Assembly B "پخته" یا "bake in" میشه. حالا اگه شما مقدار const رو در Assembly A عوض کنید، تا وقتی که Assembly B رو دوباره کامپایل نکنید، همچنان از همون مقدار قدیمی استفاده خواهد کرد! این میتونه منجر به باگهای فاجعهبار بشه. static readonly این مشکل رو نداره و همیشه آخرین مقدار رو میخونه، بنابراین انتخاب امنتریه.
🤔 حرف حساب و تجربه شما
انتخاب درست بین const و static readonly یکی از نشانههای یه توسعهدهنده باتجربهست که به نگهداری بلندمدت کد فکر میکنه.
شما بیشتر از const استفاده میکنید یا static readonly؟ آیا تا حالا به مشکل اسمبلی با const برخورد کرده بودید؟
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #OOP #CleanCode #BestPractices
🔬 کالبدشکافی متدها در #C: امضا (Signature) و Overloading
هر متدی در #C یک "اثر انگشت" یا امضای (Signature) منحصر به فرد داره. این امضا هویت متد رو مشخص میکنه و به کامپایلر اجازه میده بین چندین متد با اسم یکسان، تفاوت قائل بشه.
امروز میخوایم این مفهوم حیاتی و قوانین Overloading رو کالبدشکافی کنیم.
1️⃣ امضای متد (Method Signature) چیست؟ 🆔
امضای یک متد از دو بخش اصلی تشکیل شده:
• اسم متد
• نوع و ترتیب پارامترها
نکات کلیدی و تلهها: ⚠️
موارد زیر جزو امضای متد حساب نمیشن:
❌ نوع خروجی (Return Type)
❌ کلمه کلیدی params
❌ اسم پارامترها (فقط نوع و ترتیبشون مهمه)
اما کلمات کلیدی ref و out جزو امضا حساب میشن!
2️⃣ اورلودینگ (Overloading): یک اسم، چند کاربرد 🎭
اورلودینگ یعنی شما میتونید چند متد با اسم یکسان در یک کلاس داشته باشید، به شرطی که امضاشون متفاوت باشه (یعنی تعداد، نوع یا ترتیب پارامترهاشون فرق کنه).
مثال Overloadهای معتبر: ✅
// اینها همگی Overloadهای معتبری از Foo هستن
void Foo(int x) { }
void Foo(double x) { } // نوع پارامتر فرق داره
void Foo(int x, float y) { } // تعداد پارامترها فرق داره
void Foo(float x, int y) { } // ترتیب انواع پارامتر فرق داره
void Foo(ref int x) { } // وجود ref امضا رو تغییر میده
مثال Overloadهای نامعتبر: ❌
// ❌ خطای زمان کامپایل!
// چون فقط نوع خروجی فرق میکنه که جزو امضا نیست.
void Foo(int x) { }
float Foo(int x) { }
// ❌ خطای زمان کامپایل!
// چون params جزو امضا نیست.
void Goo(int[] x) { }
void Goo(params int[] x) { }
// ❌ خطای زمان کامپایل!
// ref و out نمیتونن تنها تفاوت امضا باشن.
void Another(ref int x) { }
void Another(out int x) { }
🤔 حرف حساب و تجربه شما
درک دقیق قوانین امضا و اورلودینگ، به شما کمک میکنه APIهای تمیز، خوانا و قابل فهمی طراحی کنید.
آیا تا حالا با خطای کامپایل به خاطر امضای تکراری متدها برخورد کردید؟ یا از Overloading برای ساختن متدهای انعطافپذیرتر در کدهاتون استفاده میکنید؟
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #OOP #Methods #BestPractices
🛠 متدهای مدرن در #C
Expression-bodied و Local Methods
سیشارپ مدرن، پر از قابلیتهای شیک و کاربردیه که به ما کمک میکنه کدهای کوتاهتر، خواناتر و منظمتری بنویسیم. امروز با دو تا از این تکنیکهای خفن برای کار با متدها آشنا میشیم.
1️⃣ متدهای Expression-bodied (=>): خداحافظی با return و {}
اگه متد شما فقط از یک عبارت (Expression) تشکیل شده، دیگه نیازی به نوشتن بلوک {} و کلمه کلیدی return ندارید! میتونید با استفاده از "فت ارو" (=>)، اون رو در یک خط بنویسید.
این کار، کد شما رو فوقالعاده تمیز و مینیمال میکنه.
// روش قدیمی
int Double(int x)
{
return x * 2;
}
// روش مدرن و شیک با Expression-bodied
int DoubleModern(int x) => x * 2;
// این قابلیت برای متدهای void هم کار میکنه
void SayHello() => Console.WriteLine("Hello!");
2️⃣ متدهای محلی (Local Methods): متد در دل متد!
گاهی وقتا یه منطق کمکی دارید که فقط و فقط داخل یک متد دیگه استفاده میشه. به جای اینکه اون رو به عنوان یه متد private تو کل کلاس تعریف کنید و کلاس رو شلوغ کنید، میتونید اون رو به عنوان یه متد محلی، دقیقاً داخل همون متدی که بهش نیاز دارید، تعریف کنید.
مزایای این کار:
• کپسولهسازی عالی: اون متد کمکی، فقط همونجا قابل دسترسه و هیچ جای دیگهای از کلاس رو آلوده نمیکنه.
• دسترسی به متغیرهای محلی: متد محلی میتونه به متغیرهای محلی و پارامترهای متد بیرونی دسترسی داشته باشه.
void WriteCubes()
{
Console.WriteLine(Cube(3));
Console.WriteLine(Cube(4));
// این متد، فقط داخل WriteCubes قابل دسترسه
int Cube(int value) => value * value * value;
}
💡نکته حرفهای (static local methods):
از C# 8 به بعد، اگه متد محلی شما نیازی به دسترسی به متغیرهای متد بیرونی نداره، میتونید اون رو با کلمه کلیدی static تعریف کنید. این کار به کامپایلر کمک میکنه بهینهسازیهای بهتری انجام بده و جلوی دسترسیهای ناخواسته رو هم میگیره.
🤔 حرف حساب و تجربه شما
این تکنیکهای مدرن، ابزارهای شما برای نوشتن کدهایی هستن که هم کار میکنن و هم خوندنشون لذتبخشه.
شما از کدوم یکی از این قابلیتها بیشتر استفاده میکنید؟ آیا متدهای Expression-bodied جزو استایل کدنویسی شما هستن؟
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #CleanCode #ModernCSharp #BestPractices
🏗 سازندهها (Constructors) در #C: معمار آبجکتهای شما
هر آبجکتی در #C یه "لحظه تولد" داره. اون لحظه، کدی اجرا میشه که آبجکت رو برای زندگی آماده میکنه. به این کد جادویی میگن سازنده (Constructor).
سازنده، اولین متدیه که موقع ساختن یک نمونه جدید از کلاس با کلمه کلیدی new اجرا میشه. امروز میخوایم با تمام زیر و بم این معماران آبجکت آشنا بشیم.
1️⃣ سازنده چیست و چطور کار میکند؟
سازنده شبیه یک متده، با این تفاوت که اسمش دقیقاً هماسم خود کلاس هست و هیچ نوع خروجیای (حتی void) نداره. کار اصلیش، مقداردهی اولیه فیلدها و آمادهسازی آبجکته.
public class Panda
{
string _name; // فیلد
// این سازنده است
public Panda(string name)
{
_name = name; // کد مقداردهی اولیه
}
}
// ... نحوه استفاده
Panda p = new Panda("Petey"); // سازنده اینجا صدا زده میشه
💡نکته حرفهای: برای سازندههای تکخطی، میتونید از سینتکس Expression-bodied استفاده کنید:
public Panda(string name) => _name = name;
ترفند this: اگه اسم پارامتر با اسم فیلد یکی بود، میتونید با کلمه کلیدی this به فیلد کلاس اشاره کنید:
public Panda(string name) => this.name = name;
2️⃣ اورلودینگ و زنجیرهای کردن سازندهها (this):
شما میتونید چندین سازنده با ورودیهای مختلف (Overloading) داشته باشید. برای جلوگیری از تکرار کد، یک سازنده میتونه اون یکی رو با کلمه کلیدی : this(...) صدا بزنه.
public class Wine
{
public decimal Price;
public int Year;
public Wine(decimal price)
{
Price = price;
}
// این سازنده، قبل از اجرای بدنه خودش، سازنده بالایی رو صدا میزنه
public Wine(decimal price, int year) : this(price)
{
Year = year;
}
}
3️⃣ تلهی سازنده پیشفرض! ⚠️
قانون مهم: کامپایلر #C فقط و فقط زمانی که شما هیچ سازندهای در کلاس تعریف نکرده باشید، یه سازنده عمومی بدون پارامتر ( ()public MyClass) براتون میسازه.
به محض اینکه شما حتی یک سازنده (با یا بدون پارامتر) بنویسید، اون سازنده اتوماتیک حذف میشه و اگه بهش نیاز دارید، باید خودتون دستی بنویسیدش. این یکی از خطاهای رایج برای تازهکارهاست!
4️⃣ ترتیب اجرا: اول فیلدها، بعد سازنده
اگه فیلدهاتون مقدار اولیه دارن، این مقداردهی همیشه قبل از اجرای اولین خط کد در سازنده و به ترتیب تعریفشون در کلاس انجام میشه.
class Player
{
int shields = 50; // این اول اجرا میشه
int health = 100; // این دوم اجرا میشه
public Player()
{
// این سوم اجرا میشه
Console.WriteLine("Constructor executed!");
}
}
5️⃣ کاربرد حرفهای: سازندههای private
چرا باید یه سازنده رو private کنیم؟ برای اینکه کنترل کامل ساخت آبجکت رو به دست بگیریم! این الگو (که بهش Factory Method میگن) به ما اجازه میده که منطق ساخت رو داخل یه متد استاتیک قرار بدیم. مثلا برای پیادهسازی الگوی Singleton یا برگردوندن آبجکت از یک Pool.
public class MyApiClien
{
// سازنده خصوصیه، پس کسی از بیرون نمیتونه new کنه
private MyApiClient() { }
// فقط از طریق این متد استاتیک میشه نمونه ساخت
public static MyApiClient Create()
{
// ... منطق پیچیده برای ساخت و کانفیگ ...
return new MyApiClient();
}
}
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #OOP #Constructor #BestPractices
✨ ساخت آبجکت مثل یک حرفهای: قدرت Object Initializers در #C
یادتونه قدیما برای ساختن و مقداردهی یه آبجکت، باید چند خط کد پشت سر هم مینوشتیم؟ این روش هم طولانیه و هم ممکنه باعث بشه مقداردهی بعضی پراپرتیها رو فراموش کنیم.
سیشارپ یه راه حل خیلی شیک، مدرن و امن برای این کار داره: Object Initializers.
1️⃣ روش سنتی در برابر روش مدرن
فرض کنید این کلاس Bunny رو داریم:
public class Bunny
{
public string Name;
public bool LikesCarrots;
public bool LikesHumans;
public Bunny() {}
public Bunny(string n) => Name = n;
}
روش قدیمی و چند خطی: 👎
Bunny b1 = new Bunny();
b1.Name = "Bo";
b1.LikesCarrots = true;
b1.LikesHumans = false;
روش مدرن با Object Initializer: 👍
حالا با سینتکس {}، میتونیم تمام این کارها رو در یک دستور و به صورت خیلی خوانا انجام بدیم:
Bunny b2 = new Bunny
{
Name = "Bo",
LikesCarrots = true,
LikesHumans = false
};
این قابلیت حتی با سازندههایی که پارامتر دارن هم کار میکنه:
Bunny b3 = new Bunny("Bo")
{
LikesCarrots = true,
LikesHumans = false
};2️⃣ پشت صحنه چه خبره؟ (نکته حرفهای) ⚙️
شاید فکر کنید این سینتکس فقط یه خلاصه نویسیه، ولی کامپایلر پشت صحنه یه کار هوشمندانه برای امنیت در برابر خطاها (Exception Safety) انجام میده.
اون اول آبجکت رو تو یه متغیر موقت میسازه و بعد پراپرتیها رو ست میکنه. این کار تضمین میکنه که اگه وسط مقداردهی یکی از پراپرتیها خطایی رخ بده، شما هیچوقت با یه آبجکت نصفه و نیمه و ناقص مواجه نمیشید!
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #OOP #CleanCode #BestPractices
🔬 کالبدشکافی آبجکتها با Deconstructors در #C
تو پست قبلی دیدیم چطور با Object Initializers یه آبجکت رو به صورت شیک "بسازیم". حالا بیاید ببینیم چطور میتونیم یه آبجکت رو به همون زیبایی "بشکافیم" و اجزاش رو استخراج کنیم!
سیشارپ یه قابلیت مدرن و قدرتمند به اسم Deconstructor داره که دقیقاً برعکس سازنده (Constructor) عمل میکنه.
1️⃣ حالا Deconstructor چیست؟
این Deconstructor یه متد خاص به اسم Deconstruct هست که شما تو کلاستون تعریف میکنید. این متد، فیلدها و پراپرتیهای کلاس شما رو به مجموعهای از متغیرهای خروجی (out parameters) تبدیل میکنه.
public class Rectangle
{
public readonly float Width, Height;
public Rectangle(float width, float height)
{
Width = width;
Height = height;
}
// این متد Deconstructor ماست
public void Deconstruct(out float width, out float height)
{
width = Width;
height = Height;
}
}
2️⃣ جادوی سینتکس: کالبدشکافی در یک خط! ✨
حالا که متد Deconstruct رو داریم، #C به ما یه سینتکس فوقالعاده شیک و خوانا برای استفاده ازش میده:
var rect = new Rectangle(3, 4);
// جادو اینجا اتفاق میفته!
// این خط، متد Deconstruct رو صدا میزنه
(float width, float height) = rect;
Console.WriteLine($"Width: {width}, Height: {height}"); // خروجی: Width: 3, Height: 4
این سینتکس، کد شما رو به شدت تمیز و بیانگر میکنه.
3️⃣ ترفندهای خلاصهنویسی
این قابلیت چند تا ترفند برای خلاصهتر شدن هم داره:
استفاده از var:
// کامپایلر خودش نوعها رو تشخیص میده
var (width, height) = rect;
نادیده گرفتن با _ (Discard):
اگه فقط به یکی از مقادیر نیاز دارید، میتونید اون یکی رو با _ نادیده بگیرید:
// ما اینجا فقط به ارتفاع نیاز داریم
var (_, height) = rect;
تخصیص به متغیرهای موجود:
اگه متغیرها رو از قبل دارید، دیگه نیازی به تعریفشون نیست:
float w, h;
(w, h) = rect;
🤔 حرف حساب و تجربه شما
شما تا حالا از Deconstructors تو کدهاتون استفاده کردید؟ به نظرتون بهترین کاربرد این قابلیت کجاست؟
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #ModernCSharp #CleanCode #BestPractices
جمعبندی 📝
معرفی فیلترهای کوئری نامدار در EF 10 یکی از محدودیتهای دیرینه را برطرف میکند. شما اکنون میتوانید:
🔹 چندین فیلتر را به یک انتیتی متصل کرده و آنها را به صورت جداگانه مدیریت کنید.
🔹 فیلترهای خاصی را در یک کوئری LINQ با استفاده از IgnoreQueryFilters(["FilterName"]) به صورت انتخابی غیرفعال کنید.
🔹 الگوهای رایجی مانند حذف منطقی و چند-مستأجری را سادهسازی کنید.
فیلترهای کوئری نامدار میتوانند به ابزاری قدرتمند برای تمیز نگه داشتن کوئریها و کپسولهسازی منطق دامین شما تبدیل شوند.
آنها را در نسخه پیشنمایش امتحان کنید و به این فکر کنید که چگونه میتوانند پایگاه کد شما را سادهتر کنند.
معرفی فیلترهای کوئری نامدار در EF 10 یکی از محدودیتهای دیرینه را برطرف میکند. شما اکنون میتوانید:
🔹 چندین فیلتر را به یک انتیتی متصل کرده و آنها را به صورت جداگانه مدیریت کنید.
🔹 فیلترهای خاصی را در یک کوئری LINQ با استفاده از IgnoreQueryFilters(["FilterName"]) به صورت انتخابی غیرفعال کنید.
🔹 الگوهای رایجی مانند حذف منطقی و چند-مستأجری را سادهسازی کنید.
فیلترهای کوئری نامدار میتوانند به ابزاری قدرتمند برای تمیز نگه داشتن کوئریها و کپسولهسازی منطق دامین شما تبدیل شوند.
آنها را در نسخه پیشنمایش امتحان کنید و به این فکر کنید که چگونه میتوانند پایگاه کد شما را سادهتر کنند.
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #EntityFrameWork #EF
💡 نکته عملکردی C#/.NET – آرایههای Inline 🔥
💎 آرایههای Inline چیستند؟
⚡️ در C# 12 و NET 8.0. معرفی شدهاند. آرایههای inline به ما اجازه میدهند که یک آرایه با اندازه ثابت در یک نوع ساختاری (struct) ایجاد کنیم. این آرایهها توسط تیم Runtime و نویسندگان دیگر کتابخانهها برای بهبود عملکرد در برنامههای شما استفاده میشوند.
⚡️ از نظر عملکرد در کنسول، تغییر خاصی در کارکرد ایجاد نشده است. یک struct با یک آرایه inline باید ویژگیهای عملکردی مشابه یک بافر ثابت (fixed size buffer) ناایمن (unsafe) داشته باشد.
💡 برخلاف آرایههای پویا (dynamic) سنتی، آرایههای inline در فضای حافظه همان struct قرار میگیرند. این جایگیری منحصربهفرد چند مزیت کلیدی را فراهم میکند.
✅ چند مزیت آرایههای Inline:
🔸 بهبود عملکرد: با حذف تخصیص حافظه روی heap و استفاده از حافظه stack، آرایههای inline سرعت اجرای توابع را به شکل قابلتوجهی افزایش میدهند و فشار کلی روی حافظه را کاهش میدهند.
🔸 مدیریت حافظه سادهتر: دیگر نیازی به تخصیص صریح یا نگرانی درباره جمعآوری زباله (Garbage Collection) نیست. آرایههای inline بهطور یکپارچه در structها ادغام میشوند و شما را از دردسر مدیریت حافظه رها میکنند.
🔸 ایمنی نوع قویتر: بررسیهای زمان کامپایل برای اندازه آرایه و نوع عناصر، یک لایه حفاظتی اضافی در برابر خطاهای زمان اجرا ایجاد میکنند.
🔖هشتگها:
#csharp #dotnet #programming #softwareengineering #softwaredevelopment
🚀 جادوی async/await در #C:
برنامهنویسی غیرهمزمان به زبان ساده
تا حالا شده یه دکمه تو اپلیکیشنتون بزنید و کل برنامه برای چند ثانیه هنگ کنه و سفید بشه؟ 🥶 این اتفاق وقتی میفته که یه کار زمانبر (مثل دانلود فایل یا کوئری دیتابیس) رو به صورت همزمان (Synchronous) انجام میدید و "نخ" اصلی برنامه رو قفل میکنید.
راه حل این کابوس، برنامهنویسی غیرهمزمان (Asynchronous) با دو کلمه کلیدی جادویی async و await هست.
1️⃣ داستان سرآشپز صبور (آنالوژی) 👨🍳
برای درک این مفهوم، یه رستوران رو تصور کنید:
سرآشپز همزمان (Synchronous): 👎
شما سفارش استیک میدید. سرآشپز استیک رو روی گریل میذاره و همونجا وایمیسته و ۱۰ دقیقه بهش زل میزنه تا بپزه. تو این ۱۰ دقیقه، هیچ کار دیگهای نمیتونه بکنه و بقیه مشتریها گشنه میمونن. این یعنی قفل شدن برنامه!
سرآشپز غیرهمزمان (Asynchronous): 👍
شما سفارش استیک میدید. سرآشپز استیک رو روی گریل میذاره (await) و بلافاصله از آشپزخونه میاد بیرون و سفارش بقیه رو میگیره. اون یه "قول" (Task) داره که استیک در حال آماده شدنه. ۱۰ دقیقه بعد، وقتی استیک آماده شد، برمیگرده سراغش، کار رو تموم میکنه و به شما تحویل میده. تو این مدت، رستوران (برنامه) کاملاً فعال و پاسخگو بوده!
2️⃣ کلمات کلیدی جادویی: async, await, Task ✨
این سه تا با هم کار میکنن:
• async:
یه برچسبه که به متد میزنیم و به کامپایلر میگیم: "هی، این متد ممکنه وسط کارش منتظر یه چیزی بمونه و غیرهمزمانه". این کلمه، اجازه استفاده از await رو داخل متد میده.
• Task / Task<T>:
"قول" یا "رسیدی" هست که متد async فوراً برمیگردونه.
• Task:
یعنی "قول میدم این کار رو تموم کنم." (برای متدهایی که خروجی ندارن).
• Task<T>:
یعنی "قول میدم این کار رو تموم کنم و یه نتیجه از نوع T بهت تحویل بدم."
• await:
قلب ماجراست! این کلمه رو قبل از صدا زدن یه متد async دیگه میذاریم و به برنامه میگه:
"اجرای این کار زمانبر رو شروع کن، و تا وقتی تموم میشه، کنترل رو به کسی که منو صدا زده برگردون تا اون بتونه به کاراش برسه! وقتی کارم تموم شد، از همین خط به بعد ادامه میدم."
3️⃣ مثال عملی: دانلود کردن یک فایل
روش بد (Synchronous) که باعث هنگ کردن برنامه میشه:
// ❌ این متد نخ اصلی رو برای ۵ ثانیه قفل میکنه!
public void DownloadFile()
{
Thread.Sleep(5000); // شبیهسازی یک عملیات زمانبر و مسدودکننده
Console.WriteLine("Download complete.");
}
روش خوب (Asynchronous) با async/await:
// ✅ این متد نخ اصلی رو آزاد میکنه و برنامه پاسخگو باقی میمونه
public async Task DownloadFileAsync()
{
// await کنترل رو به بیرون برمیگردونه و منتظر میمونه
await Task.Delay(5000); // شبیهسازی یک عملیات غیرهمزمان
Console.WriteLine("Download complete.");
}
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #Async #Await #Concurrency
📌در این مثال:
• کلید کش برای هر محصول منحصر به فرد است.
• اگر محصول در کش باشد، بلافاصله برگردانده میشود.
• اگر نباشد، متد factory برای گرفتن داده اجرا میشود.
• درخواستهای همزمان دیگر برای همان محصول، منتظر پایان یافتن اولین درخواست میمانند.
گاهی اوقات نیاز دارید کش را مستقیماً بهروزرسانی کنید، مانند پس از تغییر دادهها. متد SetAsync این کار را انجام میدهد:
تگها برای مدیریت گروههایی از ورودیهای کش مرتبط، قدرتمند هستند. شما میتوانید چندین ورودی را به یکباره با استفاده از تگها نامعتبر کنید:
• نامعتبر کردن تمام محصولات در یک دستهبندی.
• پاک کردن تمام دادههای کش شده برای یک کاربر خاص.
• رفرش کردن تمام دادههای مرتبط هنگامی که چیزی تغییر میکند.
برای نامعتبرسازی مستقیم آیتمهای خاص، از RemoveAsync استفاده کنید:
برای استفاده از Redis به عنوان کش توزیعشده خود:
پکیج NuGet را نصب کنید:
Install-Package Microsoft.Extensions.Caching.StackExchangeRedis
Redis و HybridCache را پیکربندی کنید:
HybridCache
کشینگ را در اپلیکیشنهای NET. ساده میکند. این قابلیت، کشینگ سریع درون-حافظهای را با کشینگ توزیعشده ترکیب میکند، از مشکلات رایج مانند cache stampede جلوگیری میکند، و هم در سیستمهای تک-سروری و هم توزیعشده به خوبی کار میکند.
با تنظیمات پیشفرض و الگوهای استفاده اولیه شروع کنید - این کتابخانه طوری طراحی شده که استفاده از آن ساده باشد در حالی که مشکلات پیچیده کشینگ را حل میکند.
• کلید کش برای هر محصول منحصر به فرد است.
• اگر محصول در کش باشد، بلافاصله برگردانده میشود.
• اگر نباشد، متد factory برای گرفتن داده اجرا میشود.
• درخواستهای همزمان دیگر برای همان محصول، منتظر پایان یافتن اولین درخواست میمانند.
تنظیم مستقیم ورودیهای کش ✍️
گاهی اوقات نیاز دارید کش را مستقیماً بهروزرسانی کنید، مانند پس از تغییر دادهها. متد SetAsync این کار را انجام میدهد:
app.MapPut("/products/{id}", async (int id, Product product, HybridCache cache) =>
{
// ابتدا دیتابیس را بهروزرسانی کنید
await UpdateProductInDatabase(product);
// سپس کش را با انقضای سفارشی بهروزرسانی کنید
var options = new HybridCacheEntryOptions
{
Expiration = TimeSpan.FromHours(1),
LocalCacheExpiration = TimeSpan.FromMinutes(30)
};
await cache.SetAsync(
$"product-{id}",
product,
options
);
return Results.NoContent();
});استفاده از تگهای کش 🏷
تگها برای مدیریت گروههایی از ورودیهای کش مرتبط، قدرتمند هستند. شما میتوانید چندین ورودی را به یکباره با استفاده از تگها نامعتبر کنید:
app.MapGet("/categories/{id}/products", async (...) =>
{
var tags = [$"category-{id}", "products"];
var products = await cache.GetOrCreateAsync(
$"products-by-category-{id}",
async token => { /* ... fetch products ... */ },
tags: tags,
cancellationToken: ct
);
return Results.Ok(products);
});
// Endpoint برای نامعتبر کردن تمام محصولات در یک دستهبندی
app.MapPost("/categories/{id}/invalidate", async (id, cache, ct) =>
{
await cache.RemoveByTagAsync($"category-{id}", ct);
return Results.NoContent();
});📍تگها برای موارد زیر مفید هستند:
• نامعتبر کردن تمام محصولات در یک دستهبندی.
• پاک کردن تمام دادههای کش شده برای یک کاربر خاص.
• رفرش کردن تمام دادههای مرتبط هنگامی که چیزی تغییر میکند.
حذف ورودیهای تکی 🗑
برای نامعتبرسازی مستقیم آیتمهای خاص، از RemoveAsync استفاده کنید:
app.MapDelete("/products/{id}", async (int id, HybridCache cache) =>
{
// ابتدا از دیتابیس حذف کنید
await DeleteProductFromDatabase(id);
// سپس از کش حذف کنید
await cache.RemoveAsync($"product-{id}");
return Results.NoContent();
});افزودن Redis به عنوان کش L2 🔥
برای استفاده از Redis به عنوان کش توزیعشده خود:
پکیج NuGet را نصب کنید:
Install-Package Microsoft.Extensions.Caching.StackExchangeRedis
Redis و HybridCache را پیکربندی کنید:
// افزودن Redis
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "your-redis-connection-string";
});
// افزودن HybridCache - به طور خودکار از Redis به عنوان L2 استفاده خواهد کرد
builder.Services.AddHybridCache();
خلاصه 📝
HybridCache
کشینگ را در اپلیکیشنهای NET. ساده میکند. این قابلیت، کشینگ سریع درون-حافظهای را با کشینگ توزیعشده ترکیب میکند، از مشکلات رایج مانند cache stampede جلوگیری میکند، و هم در سیستمهای تک-سروری و هم توزیعشده به خوبی کار میکند.
با تنظیمات پیشفرض و الگوهای استفاده اولیه شروع کنید - این کتابخانه طوری طراحی شده که استفاده از آن ساده باشد در حالی که مشکلات پیچیده کشینگ را حل میکند.
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #HybridCache
📖 سری آموزشی کتاب C# 12 in a Nutshell
'خود' آبجکت کیست؟ در دنیای شیءگرایی، گاهی وقتا یه آبجکت نیاز داره به "خودش" اشاره کنه. ابزار #C برای این کار، کلمه کلیدی ساده ولی قدرتمند this هست. this در واقع ضمیر "من" برای یک آبجکته و به نمونه فعلی (current instance) خودش اشاره میکنه.
1️⃣ کاربرد اول: رفع ابهام (Disambiguation)
این رایجترین کاربرد this هست. وقتی اسم پارامتر سازنده، هماسم یکی از فیلدهای کلاس باشه، برای اینکه به کامپایلر بفهمونیم منظورمون فیلد کلاسه، از this استفاده میکنیم.
2️⃣ کاربرد دوم: پاس دادن خودِ آبجکت 🤝
گاهی وقتا لازمه یه آبجکت، رفرنس خودش رو به یه آبجکت یا متد دیگه بده.
this
فقط در اعضای غیر استاتیک (non-static) یک کلاس یا struct معتبره.
👇کلیدواژه this در #C:
'خود' آبجکت کیست؟ در دنیای شیءگرایی، گاهی وقتا یه آبجکت نیاز داره به "خودش" اشاره کنه. ابزار #C برای این کار، کلمه کلیدی ساده ولی قدرتمند this هست. this در واقع ضمیر "من" برای یک آبجکته و به نمونه فعلی (current instance) خودش اشاره میکنه.
1️⃣ کاربرد اول: رفع ابهام (Disambiguation)
این رایجترین کاربرد this هست. وقتی اسم پارامتر سازنده، هماسم یکی از فیلدهای کلاس باشه، برای اینکه به کامپایلر بفهمونیم منظورمون فیلد کلاسه، از this استفاده میکنیم.
public class Test
{
private string name;
public Test(string name)
{
// this.name به فیلد کلاس اشاره داره
// name به پارامتر ورودی اشاره داره
this.name = name;
}
}
2️⃣ کاربرد دوم: پاس دادن خودِ آبجکت 🤝
گاهی وقتا لازمه یه آبجکت، رفرنس خودش رو به یه آبجکت یا متد دیگه بده.
public class Panda
{
public Panda Mate;
public void Marry(Panda partner)
{
Mate = partner;
// 'خودم' رو به عنوان جفتِ شریکم معرفی میکنم
partner.Mate = this;
}
}
قانون مهم ⚠️
this
فقط در اعضای غیر استاتیک (non-static) یک کلاس یا struct معتبره.
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #OOP #ThisKeyword
📖 سری آموزشی کتاب C# 12 in a Nutshell
دروازههای هوشمند کلاس شما چرا تو کدنویسی حرفهای، تقریباً هیچوقت فیلدهای یه کلاس رو public نمیکنن؟ چون این کار یعنی از دست دادن کنترل! هر کسی از بیرون میتونه هر مقدار نامعتبری رو توش بریزه.
راه حل #C برای این مشکل، یه قابلیت فوقالعاده به اسم پراپرتی (Property) هست.
پراپرتیها از بیرون شبیه فیلدهای معمولی به نظر میرسن و به همون سادگی استفاده میشن، ولی در داخل، در واقع متدهای خاصی هستن که بهشون اکسسور (accessor) گفته میشه. این به ما کنترل کامل روی خوندن و نوشتن مقدار رو میده.
در رایجترین حالت، یک پراپرتی از دو بخش تشکیل شده:
• فیلد پشتیبان (Backing Field): یک فیلد private که داده واقعی رو نگه میداره.
• پراپرتی public: دروازهای که به دنیای بیرون اجازه دسترسی کنترلشده به اون فیلد رو میده.
✨️این پراپرتی، دو اکسسور داره:
🔹️get:
وقتی پراپرتی رو میخونیم، این بلوک اجرا میشه.
🔹️set:
وقتی مقداری رو به پراپرتی اختصاص میدیم، این بلوک اجرا میشه. کلمه کلیدی value در اینجا، به مقداری که داره ست میشه، اشاره داره.
جادوی پراپرتیها اینجاست که میتونید تو اکسسورهای get و set، منطق دلخواهتون رو پیاده کنید. مثلاً اعتبارسنجی (validation).
🤔 حرف حساب و قانون طلایی
قانون طلایی شیءگرایی: فیلدها رو همیشه private نگه دارید و با پراپرتیهای public اونها رو در معرض دید بذارید. این کار به شما کنترل کامل روی کلاسهاتون میده و اساس کپسولهسازیه.
🏦 پراپرتیها (Properties) در #C:
دروازههای هوشمند کلاس شما چرا تو کدنویسی حرفهای، تقریباً هیچوقت فیلدهای یه کلاس رو public نمیکنن؟ چون این کار یعنی از دست دادن کنترل! هر کسی از بیرون میتونه هر مقدار نامعتبری رو توش بریزه.
راه حل #C برای این مشکل، یه قابلیت فوقالعاده به اسم پراپرتی (Property) هست.
1️⃣ پراپرتی چیست؟ فیلد با ماسک متد! 🎭
پراپرتیها از بیرون شبیه فیلدهای معمولی به نظر میرسن و به همون سادگی استفاده میشن، ولی در داخل، در واقع متدهای خاصی هستن که بهشون اکسسور (accessor) گفته میشه. این به ما کنترل کامل روی خوندن و نوشتن مقدار رو میده.
Stock msft = new Stock();
msft.CurrentPrice = 30; // اکسسور set صدا زده میشه
msft.CurrentPrice -= 3;
Console.WriteLine(msft.CurrentPrice); // اکسسور get صدا زده میشه
2️⃣ کالبدشکافی یک پراپرتی 🔬
در رایجترین حالت، یک پراپرتی از دو بخش تشکیل شده:
• فیلد پشتیبان (Backing Field): یک فیلد private که داده واقعی رو نگه میداره.
• پراپرتی public: دروازهای که به دنیای بیرون اجازه دسترسی کنترلشده به اون فیلد رو میده.
✨️این پراپرتی، دو اکسسور داره:
🔹️get:
وقتی پراپرتی رو میخونیم، این بلوک اجرا میشه.
🔹️set:
وقتی مقداری رو به پراپرتی اختصاص میدیم، این بلوک اجرا میشه. کلمه کلیدی value در اینجا، به مقداری که داره ست میشه، اشاره داره.
public class Stock
{
// ۱. فیلد پشتیبان (private)
private decimal _currentPrice;
// ۲. پراپرتی عمومی (public)
public decimal CurrentPrice
{
get { return _currentPrice; }
set { _currentPrice = value; }
}
}
3️⃣ قدرت واقعی: کپسولهسازی (Encapsulation) 🛡
جادوی پراپرتیها اینجاست که میتونید تو اکسسورهای get و set، منطق دلخواهتون رو پیاده کنید. مثلاً اعتبارسنجی (validation).
public class Stock
{
private decimal _currentPrice;
public decimal CurrentPrice
{
get { return _currentPrice; }
set
{
// منطق اعتبارسنجی
if (value < 0)
{
throw new ArgumentException("Price cannot be negative!");
}
_currentPrice = value;
}
}
}
🤔 حرف حساب و قانون طلایی
قانون طلایی شیءگرایی: فیلدها رو همیشه private نگه دارید و با پراپرتیهای public اونها رو در معرض دید بذارید. این کار به شما کنترل کامل روی کلاسهاتون میده و اساس کپسولهسازیه.
🔖 هشتگها:
#CSharp #Programming #DotNet #OOP #Properties #Encapsulation