C# Geeks (.NET)
334 subscribers
128 photos
1 video
98 links
Download Telegram
📂 نظم و ترتیب در #C: همه چیز درباره namespace (از روش کلاسیک تا 10 #C)

وقتی پروژه‌هاتون بزرگ میشه و تعداد کلاس‌ها زیاد میشه، چطور ازش یه آش شله‌قلمکار نسازیم؟ ابزار اصلی #C برای سازماندهی و جلوگیری از تداخل اسمی، namespace هست.

این namespace مثل یه سیستم پوشه‌بندیه (folders) برای کدهاتون. شما می‌تونید کلاس‌های مرتبط رو تو یه namespace بذارید تا هم پیداکردنشون راحت‌تر باشه و هم اگه دو تا کلاس با اسم یکسان تو دو تا پوشه مختلف داشتید، با هم قاطی نشن.

1️⃣ روش کلاسیک تعریف namespace

در روش سنتی، شما یک بلوک namespace تعریف می‌کنید و کلاس‌هاتون رو داخلش قرار میدید. برای ایجاد ساختار تودرتو، می‌تونید از نقطه استفاده کنید.
// روش تودرتو با استفاده از نقطه (روش پیشنهادی)
namespace MyProject.Services.Authentication
{
class AuthService { }
class TokenService { }
}

// این کد معادل کد بالاست ولی باعث تو رفتگی (indentation) بیشتر میشه
namespace MyProject
{
namespace Services
{
namespace Authentication
{
class AuthService { }
class TokenService { }
}
}
}


برای استفاده از یک کلاس، باید اسم کاملش رو به همراه namespace وارد کنید، مگر اینکه اون namespace رو بالای فایل using کرده باشید.

2️⃣ روش مدرن (از 10 #C): File-Scoped Namespaces

تیم #C دید که معمولاً تمام کلاس‌های یه فایل، تو یه namespace مشترک هستن. پس از 10 #C به بعد، یه راه خیلی تمیزتر معرفی کردن که کل فایل رو داخل یه namespace قرار میده و از یه لایه تو رفتگی اضافی جلوگیری می‌کنه.

قبل از 10 #C:
namespace MyNamespace
{
class Class1
{
// Code...
}

class Class2
{
// Code...
}
}

از 10 #C به بعد:
namespace MyNamespace; // این برای کل فایل اعمال میشه و تمام!

class Class1
{
// Code...
}

class Class2
{
// Code...
}


این قابلیت، کد شما رو خیلی تمیزتر و خلاصه‌تر می‌کنه.

🤔 حرف حساب و تجربه شما

استفاده درست از namespaceها، اولین قدم برای نوشتن یه پروژه بزرگه که قابل نگهداری باشه.

شما تو پروژه‌هاتون چقدر روی ساختار namespaceها وقت میذارید؟ از قابلیت جدید File-Scoped Namespaces استفاده می‌کنید؟

خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامه‌ی بحث، سوالات، غر زدن‌ها و گپ و گفت‌های خودمونی، فقط تو گروه.
[C# Geeks Hangout]

🔖 هشتگ‌ها:
#CSharp #Programming #Developer #DotNet #CleanCode #BestPractices #Namespace
🏛 اصول SOLID: پنج ستون اصلی برای ساخت نرم‌افزار حرفه‌ای

تا حالا شده به کدی برگردید که چند ماه پیش نوشتید و دیگه هیچی ازش نفهمید؟ یا بخواید یه تغییر کوچیک بدید و ببینید کل برنامه به هم ریخت؟

💡این مشکلات یه راه حل معروف دارن: اصول SOLID.

اینSOLID مجموعه‌ای از پنج اصل بنیادی در طراحی شیءگراست که توسط "رابرت مارتین (عمو باب)" معرفی شدن. این اصول، مثل ستون‌های یه ساختمون بزرگ، به شما کمک می‌کنن کدی بنویسید که انعطاف‌پذیر، قابل نگهداری و توسعه‌پذیر باشه.

حالا SOLID مخفف چیست؟ 🤔
هر حرف در SOLID، نماینده یک اصله:

1️⃣ S - Single Responsibility Principle (اصل تک مسئولیتی):
یک کلاس باید فقط و فقط یک دلیل برای تغییر داشته باشه.
2️⃣ O - Open/Closed Principle (اصل باز/بسته):
کد شما باید برای "توسعه" باز، ولی برای "تغییر" بسته باشه.
3️⃣ L - Liskov Substitution Principle (اصل جایگزینی لیسکوف):
باید بتونید یک نمونه از کلاس فرزند رو به جای کلاس پدر استفاده کنید، بدون اینکه برنامه به مشکل بخوره.
4️⃣ I - Interface Segregation Principle (اصل تفکیک اینترفیس‌ها):
کلاس‌ها نباید مجبور بشن اینترفیس‌هایی رو پیاده‌سازی کنن که بهشون نیازی ندارن.
5️⃣ D - Dependency Inversion Principle (اصل وارونگی وابستگی):
ماژول‌های سطح بالا نباید به ماژول‌های سطح پایین وابسته باشن؛ هر دو باید به "انتزاع" (Abstraction) وابسته باشن.
🚀 شروع مینی-سریال جدید!
امروز، قراره یه مینی-سریال داشته باشیم و هر کدوم از این ۵ اصل رو به صورت جداگانه، با زبون ساده و مثال‌های عملی در #C کالبدشکافی کنیم.

این سری، سطح شما رو از "کدنویس" به "معمار نرم‌افزار" نزدیک‌تر می‌کنه.

با ما همراه باشید!
💬 بحث و گفتگوی بیشتر در گروه کامیونیتی:
[C# Geeks Community]

🔖 هشتگ‌ها:
#CSharp #Programming #Developer #SOLID #SoftwareArchitecture #CleanCode #BestPractices
1️⃣ اصل اول SOLID: تک مسئولیتی (Single Responsibility Principle)

تا حالا یه کلاسی نوشتید که اولش کوچیک بوده، ولی کم کم اونقدر بزرگ و پیچیده شده که دیگه دست زدن بهش ترسناک بوده؟ این مشکل معمولاً از زیر پا گذاشتن اولین و مهم‌ترین اصل SOLID یعنی اصل تک مسئولیتی (SRP) میاد.

این اصل چی میگه؟ 🎯
به زبان ساده:

✨️یک کلاس باید فقط و فقط یک دلیل برای تغییر داشته باشه.

یعنی هر کلاس باید یه وظیفه مشخص و واحد داشته باشه و فقط همون رو به بهترین شکل انجام بده.

مثال از دنیای واقعی: یه چاقوی سوئیسی رو تصور کنید که هم چاقوئه، هم پیچ‌گوشتی، هم در باز کن. شاید به نظر کارآمد بیاد، ولی هیچکدوم از کارها رو به خوبی یه ابزار تخصصی انجام نمیده. تو کدنویسی هم همینه.

مثال عملی: کد "بدبو" 👎 در برابر کد "تمیز" 👍
فرض کنید یه کلاسی برای ثبت‌نام کاربر داریم.

مثال بد (نقض اصل تک مسئولیتی):
public class UserService
{
public void RegisterUser(string username, string password)
{
if (string.IsNullOrWhiteSpace(username))
{
// مسئولیت اول: لاگ کردن خطا در فایل
File.WriteAllText("errors.log", "Username is empty");
}

// مسئولیت دوم: ثبت کاربر در دیتابیس
// ... code to save user to database ...

// مسئولیت سوم: ارسال ایمیل خوش‌آمدگویی
// ... code to send a welcome email ...
}
}


مشکل چیه؟ این کلاس الان سه تا دلیل برای تغییر داره: ۱. اگه منطق ثبت‌نام عوض بشه. ۲. اگه نحوه لاگ کردن عوض بشه. ۳. اگه متن ایمیل عوض بشه. این یعنی یه کلاس شلوغ و شکننده!

مثال خوب (رعایت اصل تک مسئولیتی):
حالا میایم و هر مسئولیت رو به یه کلاس جدا و متخصص می‌سپاریم.
// ۱. کلاسی فقط برای لاگ کردن
public class FileLogger
{
public void LogError(string message) { /* ... */ }
}

// ۲. کلاسی فقط برای ارسال ایمیل
public class EmailService
{
public void SendWelcomeEmail(string username) { /* ... */ }
}

// ۳. کلاس اصلی که حالا فقط روی منطق کاربر تمرکز داره
public class UserService
{
private readonly FileLogger _logger;
private readonly EmailService _emailService;

public UserService(FileLogger logger, EmailService emailService)
{
_logger = logger;
_emailService = emailService;
}

public void RegisterUser(string username, string password)
{
if (string.IsNullOrWhiteSpace(username))
{
_logger.LogError("Username is empty");
return;
}

// ... code to save user to database ...

_emailService.SendWelcomeEmail(username);
}
}


حالا هر بخش، کار تخصصی خودش رو انجام میده. کد تمیز، قابل تست و قابل نگهداریه!

🔖 هشتگ‌ها:
#CSharp #Programming #Developer #SOLID #SoftwareArchitecture #CleanCode #SRP
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
💾 حافظه کلاس‌ها در #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
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
🏛️ مونولیت ماژولار چیست؟ معماری هوشمندانه‌ای که قبل از میکروسرویس باید بشناسید
همیشه بحث بوده: سادگی و یکپارچگی مونولیت یا انعطاف‌پذیری و قدرت میکروسرویس؟ اما اگه یه راه سومی وجود داشته باشه که بهترین مزایای هر دو رو داشته باشه چی؟
با معماری مونولیت ماژولار (Modular Monolith) آشنا بشید؛ رویکردی که سادگی مونولیت رو با نظم و مرزبندی میکروسرویس ترکیب می‌کنه.

1️⃣ مونولیت ماژولار به زبان ساده چیه؟ 🧱
تصور کنید به جای ساختن یه ساختمون غول‌پیکر یک‌تکه (مونولیت سنتی)، یا چندین ساختمون کاملاً جدا از هم (میکروسرویس)، شما یک مجتمع آپارتمانی بزرگ می‌سازید.
کل مجتمع یک واحد یکپارچه است، ولی داخلش به واحدهای مستقل و ایزوله با دیوارهای محکم تقسیم شده. این واحدها همون ماژول‌ها هستن (مثلاً ماژول پرداخت، ماژول کاربران). هر ماژول کارکردهای مرتبط به خودش رو گروه‌بندی می‌کنه و مرزهای مشخصی داره.
💡نکته کلیدی: این واحدها (ماژول‌ها) اتصال سستی (loosely coupled) با هم دارن و فقط از طریق یک API عمومی و مشخص با هم صحبت می‌کنن، نه اینکه در جزئیات پیاده‌سازی هم دخالت کنن.


انعطاف‌پذیری در عمل: فرض کنید در فصل تعطیلات، ماژول رزرو شما نیاز به منابع بیشتری داره. در این معماری، شما می‌تونید موقتاً همون ماژول رو به صورت مستقل دیپلوی کنید و بعداً دوباره به مونولیت اصلی برش گردونید!
2️⃣ چرا "Monolith First"؟ (حتی به گفته بزرگان!) 💡
با اینکه میکروسرویس‌ها خیلی محبوبن، پیچیدگی‌های سیستم‌های توزیع‌شده (Distributed Systems) رو به همراه دارن. برای همین، خیلی از متخصصان، از جمله مارتین فاولر، میگن:

"شما نباید یک پروژه جدید را با میکروسرویس‌ها شروع کنید، حتی اگر مطمئن باشید که اپلیکیشن شما به اندازه‌ای بزرگ خواهد شد که این کار ارزشش را داشته باشد."
حتی گوگل هم در مقالات تحقیقاتی جدیدش، به ترند مونولیت ماژولار پیوسته. دلیل این امر، چالش‌های ذاتی میکروسرویس‌هاست:
1️⃣ پرفورمنس: سربار شبکه و سریال‌سازی داده‌ها، عملکرد رو کاهش میده.
2️⃣ صحت: تضمین صحت یک سیستم توزیع‌شده بسیار دشواره.
3️⃣ مدیریت: شما باید چندین اپلیکیشن مختلف با چرخه‌های انتشار متفاوت رو مدیریت کنید.
4️⃣ بعدی APIهای منجمد: تغییر APIها بدون شکستن سرویس‌های دیگه، سخت میشه.
5️⃣ سرعت توسعه: یک تغییر کوچک در یک سرویس، ممکنه نیازمند تغییر و هماهنگی در چندین سرویس دیگه باشه.

حالا که فهمیدیم مونولیت ماژولار چیه و چرا یک انتخاب هوشمندانه‌ست، در قسمت بعدی این سری به صورت عمیق به مزایای مشخص اون می‌پردازیم و اون رو مستقیماً با میکروسرویس‌ها مقایسه می‌کنیم.

🔖 هشتگ‌ها:
#SoftwareArchitecture #CSharp #DotNet #Monolith #Microservices #Developer #CleanCode
⚖️ دوئل معماری: مزایای مونولیت ماژولار در برابر میکروسرویس‌ها

حالا بیاید ببینیم این معماری در عمل چه مزایای ملموسی داره و تفاوت اصلیش با میکروسرویس‌ها چیه.

1️⃣ مهم‌ترین مزایای مونولیت ماژولار

🚀 استقرار ساده‌شده:
برخلاف میکروسرویس‌ها که نیازمند زیرساخت و استراتژی‌های پیچیده هستن، یک مونولیت ماژولار به سادگی و به عنوان یک واحد دیپلوی میشه.

⚡️ پرفورمنس بالا:
ارتباط بین ماژول‌ها به صورت درون-فرآیندی (in-process) و مستقیمه. این یعنی هیچ تأخیر شبکه یا سربار سریال‌سازی داده‌ای که در میکروسرویس‌ها وجود داره، اینجا نیست.

💻 سرعت توسعه بالا:
شما با یک پایگاه کد واحد سر و کار دارید. این کار دیباگ کردن، تست و تجربه کلی توسعه رو به شدت ساده و سریع می‌کنه.

🔗 مدیریت تراکنش آسان:
مدیریت تراکنش‌ها در سیستم‌های توزیع‌شده یه کابوس واقعیه. اما در مونولیت ماژولار، چون ماژول‌ها می‌تونن از یک دیتابیس مشترک استفاده کنن، این کار خیلی ساده‌تره.

🛠️ پیچیدگی عملیاتی کمتر:
شما فقط یک اپلیکیشن رو مدیریت، مانیتور و دیپلوی می‌کنید، نه ده‌ها سرویس مختلف رو.

🌱 مسیر هموار به سمت میکروسرویس:
اگه در آینده نیاز شد، به راحتی می‌تونید یه ماژول رو از ساختار اصلی جدا کنید و به یه سرویس مستقل تبدیلش کنید! این بزرگترین مزیت بلندمدت این معماریه.

2️⃣ مقایسه نهایی: مونولیت ماژولار در برابر میکروسرویس‌ها

بزرگترین تفاوت در نحوه استقرار (Deployment) اون‌هاست. میکروسرویس‌ها مرزهای منطقی داخل یه مونولیت ماژولار رو به مرزهای فیزیکی ارتقا میدن.
• مونولیت ماژولار به شما میده:
انسجام بالا، اتصال سست، کپسوله‌سازی داده و تمرکز بر کارکردهای بیزینسی.

•میکروسرویس‌ها به شما میده:
تمام موارد بالا + دیپلوی مستقل، مقیاس‌پذیری مستقل و توانایی استفاده از پشته‌های فناوری (tech stacks) مختلف برای هر سرویس.

3️⃣حرف آخر: نقل قول طلایی 🎯

همونطور که سایمون براون میگه:
"میکروسرویس‌ها را به خاطر مزایایشان انتخاب کنید، نه به این دلیل که پایگاه کد مونولیت شما یک فاجعه است."


مونولیت ماژولار به شما اجازه میده ابتدا خانه‌ی خود را مرتب کنید و بعداً تصمیم بگیرید که آیا واقعاً به چندین خانه جداگانه نیاز دارید یا نه.
🤔 نظر شما چیه؟
شما تو پروژه‌هاتون تجربه کار با مونولیت ماژولار رو داشتید؟ به نظرتون بزرگترین مزیت یا چالش این معماری چیه؟[منبع]

🔖 هشتگ‌ها:
#SoftwareArchitecture #CSharp #DotNet #Monolith #Microservices #Developer #CleanCode
ساخت آبجکت مثل یک حرفه‌ای: قدرت 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
مدیریت متمرکز پکیج‌ها (CPM) در NET. : یک بار برای همیشه!


روزهایی رو به یاد میارم که مدیریت پکیج‌های NuGet در چندین پروژه یک دردسر واقعی بود. میدونید چی میگم - یه سولوشن بزرگ رو باز می‌کنی و می‌بینی هر پروژه از یه نسخه متفاوت از همون پکیج استفاده می‌کنه. اصلاً جالب نیست! 😫

بذارید بهتون نشون بدم که چطور مدیریت متمرکز پکیج‌ها (CPM) در NET. می‌تونه این مشکل رو یک بار برای همیشه حل کنه.

مشکلی که باید حل کنیم 💥
من اغلب با سولوشن‌هایی کار می‌کنم که پروژه‌های زیادی دارن. سولوشن‌هایی با ۳۰ یا بیشتر پروژه غیرمعمول نیستن. هر کدوم به پکیج‌های مشابهی مثل Serilog یا Polly نیاز دارن. قبل از CPM، پیگیری نسخه‌های پکیج یه افتضاح بود:

🔹 یک پروژه از Serilog 4.1.0 استفاده می‌کرد.

🔹 دیگری از Serilog 4.0.2.

🔹 و یه جوری، سومی از Serilog 3.1.1!

نسخه‌های مختلف می‌تونن رفتار متفاوتی داشته باشن که منجر به باگ‌های عجیبی میشه که پیدا کردنشون سخته. من ساعت‌های زیادی رو برای رفع مشکلات ناشی از عدم تطابق نسخه‌ها تلف کرده‌ام.

مدیریت متمرکز پکیج‌ها چگونه کمک می‌کند؟ 🎮

CPM
رو مثل یک مرکز کنترل برای تمام نسخه‌های پکیج‌تون در نظر بگیرید. به جای تنظیم نسخه‌ها در هر پروژه، اون‌ها رو یک بار در یک جا تنظیم می‌کنید. بعد، فقط به پکیجی که می‌خواید استفاده کنید، بدون مشخص کردن نسخه، ارجاع میدید. به همین سادگی!

برای استفاده از مدیریت متمرکز پکیج‌ها به این موارد نیاز دارید:


NuGet نسخه 6.2 یا جدیدتر

.NET SDK نسخه 6.0.300 یا جدیدتر

اگر از ویژوال استودیو استفاده می‌کنید، نسخه 2022 17.2 یا جدیدتر

راه‌اندازی آن 📁

اول، یک فایل به نام Directory.Packages.props در پوشه اصلی سولوشن خود ایجاد کنید:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>

<ItemGroup>
<PackageVersion Include="Newtonsoft.Json" Version="13.0.3" />
<PackageVersion Include="Serilog" Version="4.1.0" />
<PackageVersion Include="Polly" Version="8.5.0" />
</ItemGroup>
</Project>

در فایل‌های پروژه‌تون، می‌تونید پکیج‌ها رو با استفاده از PackageReference بدون نسخه لیست کنید:
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" />
<PackageReference Include="AutoMapper" />
<PackageReference Include="Polly" />
</ItemGroup>

همین! حالا تمام پروژه‌های شما از یک نسخه پکیج یکسان استفاده خواهند کرد.

✨️کارهای باحالی که می‌تونید انجام بدید
نیاز به نسخه‌ای متفاوت برای یک پروژه دارید؟ 🎯

مشکلی نیست! فقط این رو به فایل پروژه‌تون اضافه کنید:
<PackageReference Include="Serilog" VersionOverride="3.1.1" />

📍پراپرتی VersionOverride به شما اجازه میده نسخه خاصی که می‌خواید رو تعریف کنید.

یه پکیج رو تو همه پروژه‌ها می‌خواید؟ 🌍

اگه پکیج‌هایی دارید که هر پروژه‌ای بهشون نیاز داره، می‌تونید اون‌ها رو سراسری کنید. یک GlobalPackageReference در فایل props خود تعریف کنید:
<ItemGroup>
<GlobalPackageReference Include="SonarAnalyzer.CSharp" Version="10.3.0.106239" />
</ItemGroup>

حالا هر پروژه‌ای به طور خودکار این پکیج رو دریافت می‌کنه!

مهاجرت پروژه‌های موجود به CPM 🚚

1️⃣ فایل Directory.Packages.props رو در ریشه سولوشن ایجاد کنید.
2️⃣ تمام نسخه‌های پکیج رو از فایل‌های .csproj خود به اونجا منتقل کنید.
3️⃣ ویژگی Version رو از عناصر PackageReference حذف کنید.
4️⃣ سولوشن خود را بیلد کرده و هرگونه تداخل نسخه رو برطرف کنید.
5️⃣ قبل از کامیت کردن، به طور کامل تست کنید.

همچنین یه ابزار CLI به نام CentralisedPackageConverter وجود داره که می‌تونید برای اتوماتیک کردن مهاجرت ازش استفاده کنید.

چه زمانی باید از CPM استفاده کنید؟ 🤔
من دلیل قانع‌کننده‌ای برای استفاده نکردن پیش‌فرض از این قابلیت نمی‌بینم.
من توصیه می‌کنم از CPM استفاده کنید وقتی:

• پروژه‌های زیادی دارید که پکیج‌های مشترک دارن.

• از رفع باگ‌های مربوط به نسخه خسته شده‌اید.

• می‌خواید مطمئن بشید همه از نسخه‌های یکسان استفاده می‌کنن.

جمع‌بندی 📝
نکات من برای موفقیت با مدیریت متمرکز پکیج‌ها:


💡 وقتی CPM رو به یه سولوشن موجود اضافه می‌کنید، این کار رو در یک change/PR جداگانه انجام بدید.

💡 اگه نسخه‌ای رو override می‌کنید، یه کامنت بذارید که دلیلش رو توضیح بده.

💡 نسخه‌های پکیج خود را به طور منظم برای آپدیت‌ها چک کنید.

💡 فقط پکیج‌هایی رو سراسری کنید که واقعاً همه جا بهشون نیاز دارید.

🔖 هشتگ‌ها:
#CSharp #DotNet #NuGet #DependencyManagement #BestPractices #CleanCode #Developer #VisualStudio
📖 سری آموزشی کتاب C# 12 in a Nutshell

تکنیک‌های حرفه‌ای پراپرتی در #C: از private set تا init


تو پست های قبلی با پراپرتی‌های پایه آشنا شدیم. حالا وقتشه بریم سراغ چند تا تکنیک پیشرفته‌تر و مدرن که به شما کنترل کامل روی داده‌هاتون میده و کدهاتون رو حرفه‌ای‌تر می‌کنه.

1️⃣ مقداردهی اولیه پراپرتی‌ها (Property Initializers)

در #C مدرن، شما می‌تونید مستقیماً به پراپرتی‌های خودکار (auto-properties) مقدار اولیه بدید، دقیقاً مثل فیلدها. این کار کد رو خیلی تمیزتر و خواناتر می‌کنه و دیگه نیازی نیست برای مقداردهی‌های ساده، حتماً سازنده (constructor) بنویسید.
public class ServerConfig
{
// مقداردهی اولیه یک پراپرتی خواندنی-نوشتنی
public string IpAddress { get; set; } = "127.0.0.1";
// مقداردهی اولیه یک پراپرتی فقط-خواندنی
public int Port { get; } = 8080;
public ServerConfig(int port)
{
// شما همچنان می‌تونید مقدار پراپرتی فقط-خواندنی رو در سازنده هم تغییر بدید
Port = port;
}
}


2️⃣ کنترل دسترسی با private set

گاهی وقتا می‌خواید یه پراپرتی از بیرون کلاس فقط-خواندنی باشه، ولی از داخل خود کلاس بتونید مقدارش رو تغییر بدید (مثلاً برای شمارنده‌ها یا تغییر وضعیت داخلی). اینجاست که private set وارد میشه.
public class Counter
{
public int Value { get; private set; } = 0;
public void Increment()
{
// از داخل کلاس قابل تغییر است
Value++;
}
}

// --- نحوه استفاده ---
var counter = new Counter();
counter.Increment(); // Value is now 1
// counter.Value = 10; // خطای زمان کامپایل! از بیرون قابل تغییر نیست


3️⃣ انقلاب تغییرناپذیری با init (از 9 #C) 🔒

این یکی از مهم‌ترین قابلیت‌های #C مدرن برای ساخت آبجکت‌های تغییرناپذیر (Immutable) هست.

پراپرتی init فقط موقع ساخت آبجکت (در سازنده یا با Object Initializer) قابل مقداردهیه و بعد از اون کاملاً قفل و فقط-خواندنی میشه.
public class Note
{
public int Pitch { get; init; } = 20;
public int Duration { get; init; } = 100;
}
// --- نحوه استفاده ---
// موقع ساخت، با Object Initializer قابل تغییره
var note = new Note { Pitch = 50 };

// بعد از ساخته شدن، دیگه نمیشه تغییرش داد
// note.Pitch = 200; // خطای زمان کامپایل!

نکته کلیدی برای کتابخانه‌نویس‌ها: ⚠️

اگه شما یه پراپرتی init جدید به کلاستون اضافه کنید، کدهای قدیمی که از کتابخونه شما استفاده می‌کنن، نمی‌شکنن\! ولی اگه یه پارامتر جدید (حتی اختیاری) به سازنده اضافه کنید، اون کدها دچار Binary Breaking Change میشن و باید دوباره کامپایل بشن. این باعث میشه init برای تکامل APIها خیلی امن‌تر باشه.

🤔 حرف حساب و تجربه شما

این تکنیک‌ها ابزارهای شما برای پیاده‌سازی اصل کپسوله‌سازی و ساختن کلاس‌های امن، تغییرناپذیر و قابل نگهداری هستن.

🔖 هشتگ‌ها:
#CSharp #DotNet #OOP #Properties #CleanCode #Immutability
📖 سری آموزشی کتاب C# 12 in a Nutshell

خداحافظی با کدهای تکراری: معرفی Primary Constructors در 12 #C
از نوشتن این همه کد تکراری تو سازنده‌ها خسته شدید؟
public MyClass(string name) { this.name = name; }

این الگو که توش فقط پارامترهای ورودی به فیلدها اختصاص داده میشن، خیلی رایجه. خبر خوب اینه که از 12 #C، با Primary Constructors (سازنده‌های اصلی) می‌تونیم این کار رو خیلی شیک و کوتاه انجام بدیم!

1️⃣ Primary Constructor چیست و چطور کار می‌کند؟

این یه سینتکس جدیده که به شما اجازه میده پارامترهای سازنده اصلی رو مستقیماً بعد از اسم کلاس و داخل پرانتز تعریف کنید. کامپایلر به صورت خودکار یک سازنده بر اساس این پارامترها می‌سازه.

روش قدیمی: 👎
class Person
{
string firstName, lastName; // تعریف فیلدها
public Person(string firstName, string lastName) // تعریف سازنده
{
this.firstName = firstName; // اختصاص به فیلدها
this.lastName = lastName;
}
public void Print() => Console.WriteLine(firstName + " " + lastName);
}

روش مدرن با Primary Constructor: 👍
class Person(string firstName, string lastName)
{
public void Print() => Console.WriteLine(firstName + " " + lastName);
}
// استفاده ازش هم مثل قبل سادست
Person p = new Person("Alice", "Jones");
p.Print(); // Alice Jones


2️⃣ قوانین و نکات کلیدی ⚖️

پارامترها در کل کلاس قابل دسترسن: برخلاف سازنده معمولی که پارامترهاش فقط داخل همون بلوک زنده هستن، پارامترهای Primary Constructor در تمام بدنه کلاس قابل دسترسی هستن.

• سازنده اصلی: اگه سازنده‌های دیگه‌ای تو کلاس داشته باشید، باید با : this(...) سازنده اصلی (Primary) رو صدا بزنن.

• حذف سازنده پیش‌فرض: با تعریف سازنده اصلی، دیگه سازنده پیش‌فرض بدون پارامتر به صورت خودکار ساخته نمیشه.

3️⃣ کلاس با Primary Constructor در برابر record 🆚

رکوردها هم Primary Constructor دارن، ولی با یه تفاوت بزرگ: کامپایلر برای رکوردها، به صورت پیش‌فرض برای هر پارامتر، یه پراپرتی public init-only هم میسازه.

اما برای classها این اتفاق نمیفته و پارامترها به صورت private باقی می‌مونن (مگر اینکه خودتون به صورت دستی یک پراپرتی عمومی براشون بسازید).

4️⃣ محدودیت‌ها: کی ازش استفاده نکنیم؟ ⚠️

این قابلیت برای سناریوهای ساده عالیه، ولی محدودیت‌هایی هم داره: شما نمی‌تونید منطق اضافه‌ای (مثل اعتبارسنجی) رو مستقیم به خود سازنده اصلی اضافه کنید و برای این کارها باید از سازنده‌های سنتی استفاده کنید.

🔖 هشتگ‌ها:
#CSharp #DotNet #CSharp12 #Developer #OOP #CleanCode
📖 سری آموزشی کتاب C# 12 in a Nutshell

🏗 تقسیم کدهای بزرگ با partial در #C: کلاس‌ها و متدهای چندتکه

تا حالا شده یه فایل کد اونقدر بزرگ بشه که پیدا کردن یه متد توش عذاب‌آور باشه؟ یا بخواید کدی که خودتون نوشتید رو از کدی که به صورت خودکار توسط یه ابزار (مثل دیزاینر) تولید شده، جدا کنید؟

سی‌شارپ برای این کار یه راه حل عالی و تمیز داره: کلمه کلیدی partial.

1️⃣ کلاس‌های Partial:

یک کلاس در چند فایل کلمه کلیدی partial به شما اجازه میده تعریف یک کلاس، struct یا اینترفیس رو در چند فایل مختلف تقسیم کنید. کامپایلر موقع کامپایل، تمام این تیکه‌ها رو به هم می‌چسبونه و به عنوان یک کلاس واحد در نظر می‌گیره.

مهم‌ترین کاربرد: جداسازی کدهای دست‌نویس شما از کدهای اتوماتیک.

فایل ۱ (اتوماتیک): PaymentForm.g.cs
// این بخش توسط یک ابزار ساخته شده
public partial class PaymentForm
{
// ... کنترل‌های دیزاینر و کدهای اتوماتیک
}

فایل ۲ (دست‌نویس): PaymentForm.cs
// این بخش رو شما می‌نویسید
public partial class PaymentForm
{
// ... منطق و ایونت‌های مربوط به فرم
}


2️⃣ متدهای Partial: قلاب‌های جادویی که ناپدید میشن! 🎩

این قابلیت به شما اجازه میده در یک بخش از کلاس (معمولاً کد اتوماتیک)، یک "قلاب" یا تعریف متد بدون بدنه بذارید. بعداً در بخش دیگه کلاس (کد دست‌نویس)، می‌تونید اون متد رو پیاده‌سازی کنید.

💡نکته جادویی: اگه شما هیچ پیاده‌سازی‌ای براش ارائه ندید، کامپایلر هم تعریف و هم تمام فراخوانی‌های اون متد رو از کد نهایی حذف می‌کنه! این یعنی هیچ هزینه پرفورمنسی نداره.

فایل ۱ (اتوماتیک):
public partial class PaymentForm
{
public void Submit()
{
// یه قلاب برای اعتبارسنجی قبل از ارسال
ValidatePayment(this.Amount);
// ...
}
// تعریف متد partial (بدون بدنه)
partial void ValidatePayment(decimal amount);
}

فایل ۲ (دست‌نویس):
public partial class PaymentForm
{
// پیاده‌سازی متد partial
partial void ValidatePayment(decimal amount)
{
if (amount > 1000)
{
// ... منطق اعتبارسنجی ...
}
}
}

3️⃣ متدهای Partial توسعه‌یافته (از 9 #C) 🚀

این نسخه جدیدتر، برای کار با Source Generatorها طراحی شده. در این حالت، شما تعریف متد رو می‌نویسید و انتظار دارید که Source Generator بدنه اون رو تولید کنه. این متدها دیگه اختیاری نیستن و باید پیاده‌سازی بشن و می‌تونن خروجی و پارامتر out هم داشته باشن.

🔖 هشتگ‌ها:
#CSharp #Programming #DotNet #CleanCode #Partial
📖 سری آموزشی کتاب C# 12 in a Nutshell

🪄 خداحافظی با رشته‌های جادویی: قدرت nameof در #C

تا حالا شده اسم یه پراپرتی رو تو یه رشته بنویسید (مثلاً برای Exceptionها یا INotifyPropertyChanged) و بعداً که اسم اون پراپرتی رو عوض می‌کنید، یادتون بره اون رشته رو هم آپدیت کنید و کل برنامه به باگ بخوره؟

به این رشته‌ها میگن "رشته‌های جادویی" (Magic Strings) و یکی از منابع اصلی باگ‌های پنهان هستن. #C برای حل این مشکل، یه اپراتور خیلی ساده و قدرتمند داره.

1️⃣ راه حل: اپراتور nameof

اپراتور nameof اسم هرچیزی (متغیر، متد، کلاس، پراپرتی و...) رو در زمان کامپایل به یه رشته تبدیل می‌کنه.

مزیت اصلیش چیه؟ چک شدن در زمان کامپایل!
اگه شما اسم اون متغیر یا پراپرتی رو عوض کنید (Refactor)، ویژوال استودیو به صورت خودکار تمام nameofهای مربوط به اون رو هم آپدیت می‌کنه و دیگه هیچوقت کد شما به خاطر یه رشته قدیمی، به باگ نمی‌خوره.

2️⃣ نحوه استفاده
برای متغیرهای محلی:
int userCount = 123;
string variableName = nameof(userCount); // مقدار: "userCount"

برای اعضای یک تایپ:
// برای اعضای استاتیک و غیراستاتیک کار می‌کنه
string lengthPropName = nameof(StringBuilder.Length); // مقدار: "Length"
// برای گرفتن اسم کامل
string fullName = nameof(StringBuilder) + "." + nameof(StringBuilder.Length);
// "StringBuilder.Length"


🔖 هشتگ‌ها:
#CleanCode #Refactoring
📖 سری آموزشی کتاب C# 12 in a Nutshell

📜 قراردادهای کدنویسی در #C: راهنمای کامل اینترفیس‌ها (Interfaces)

چطور می‌تونیم برای کلاس‌هامون یه "قرارداد" تعریف کنیم و بگیم "هر کلاسی که می‌خواد با من کار کنه، باید این قابلیت‌ها رو داشته باشه"؟

ابزار اصلی ما برای این کار در دنیای شیءگرایی، اینترفیس (Interface) هست. اینترفیس‌ها ستون فقرات معماری‌های تمیز، انعطاف‌پذیر و تست‌پذیر هستن.

1️⃣ اینترفیس چیست؟ یک قرارداد، بدون پیاده‌سازی

اینترفیس فقط رفتار (Behavior) رو تعریف می‌کنه، نه وضعیت (State). یعنی فقط امضای متدها و پراپرتی‌ها رو داره، نه فیلدهای داده و نه بدنه پیاده‌سازی برای اعضاش.

تفاوت‌های کلیدی با کلاس:

🔹 فقط توابع (متد، پراپرتی، ایونت، ایندکسر) را تعریف می‌کند، نه فیلدها.

🔹 اعضایش به صورت پیش‌فرض public و abstract هستند.

🔹 یک کلاس می‌تواند چندین اینترفیس را پیاده‌سازی کند (برخلاف وراثت از کلاس که فقط یکیه).

مثال (اینترفیس IEnumerator):
public interface IEnumerator
{
bool MoveNext();
object Current { get; }
void Reset();
}


2️⃣ پیاده‌سازی اینترفیس: امضای قرارداد

وقتی یه کلاس، اینترفیسی رو پیاده‌سازی می‌کنه، قول میده که برای تمام اعضای اون اینترفیس، یک پیاده‌سازی public ارائه بده.
internal class Countdown : IEnumerator
{
int count = 11;
public bool MoveNext() => count-- > 0;
public object Current => count;
public void Reset() { throw new NotSupportedException(); }
}

حالا می‌تونید یک نمونه از Countdown رو در متغیری از نوع IEnumerator بریزید:
IEnumerator e = new Countdown();
while (e.MoveNext())
{
Console.Write(e.Current + " "); // 10 9 8 7 6 5 4 3 2 1 0
}


3️⃣ جادوی پیاده‌سازی صریح (Explicit Implementation) 🎭

حالا اگه یه کلاس، دو تا اینترفیس رو پیاده‌سازی کنه که متدهایی با اسم یکسان ولی امضای متفاوت دارن، چی میشه؟ یا اگه بخوایم یه متد از اینترفیس رو از دید عمومی کلاس مخفی کنیم؟

اینجا پای پیاده‌سازی صریح (Explicit Implementation) به میدون باز میشه. در این حالت، شما اسم اینترفیس رو قبل از اسم متد میارید.

مثال (حل تداخل):
interface I1 { void Foo(); }
interface I2 { int Foo(); }
public class Widget : I1, I2
{
public void Foo() // پیاده‌سازی پیش‌فرض برای I1
{
Console.WriteLine("Widget's implementation of I1.Foo");
}

// پیاده‌سازی صریح برای حل تداخل با I1.Foo
int I2.Foo()
{
Console.WriteLine("Widget's implementation of I2.Foo");
return 42;
}
}


نکته حیاتی: ⚠️ متدی که به صورت صریح پیاده‌سازی شده، دیگه به صورت عمومی در دسترس نیست. برای صدا زدنش، باید اول آبجکت رو به اون اینترفیس خاص کست کنید:
Widget w = new Widget();
w.Foo(); // I1.Foo صدا زده میشه
((I1)w).Foo(); // I1.Foo صدا زده میشه
((I2)w).Foo(); // I2.Foo صدا زده میشه


🤔 حرف حساب و تجربه شما

اینترفیس‌ها، اساس طراحی‌های ماژولار، تست‌پذیر و مبتنی بر اصول SOLID هستن.

🔖 هشتگ‌ها:
#CSharp #DotNet #OOP #Interface #CleanCode