🧠 مغز تصمیمگیری در #C: راهنمay کامل دستور if-else
چطور به برنامهمون یاد بدیم که فکر کنه، شرایط رو بسنجه و تصمیم بگیره؟ ابزار اصلی و روزمره ما برای این کار، دستور قدرتمند و حیاتی if هست. بیاید با تمام جزئیاتش آشنا بشیم.
1️⃣ دستور if: اگر این شرط درست بود...
سادهترین حالت. یه شرط (که باید حاصلش true یا false باشه) رو داخل پرانتز بررسی میکنه. اگه شرط true بود، بلوک کد داخلش اجرا میشه. اگه false بود، کلاً ازش رد میشه.
if (5 < 2 * 3) // شرط 5 < 6 درست است (true)
{
Console.WriteLine("شرط درست بود و این پیام چاپ شد.");
}
2️⃣ دستور else: در غیر این صورت...
else همیشه به یه if وصله و میگه: "اگه شرط if بالایی false بود، به جای اون، این بلوک کد رو اجرا کن."
if (2 + 2 == 5) // شرط غلط است (false)
{
// این بخش اجرا نمیشه
Console.WriteLine("Does not compute");
}
else
{
// چون شرط بالا غلط بود، این بخش اجرا میشه
Console.WriteLine("شرط غلط بود و این پیام چاپ شد.");
}
3️⃣ زنجیرهای کردن شرطها با else if
اگه چند تا شرط مختلف رو میخوای پشت سر هم چک کنی، از else if استفاده میکنی. #C شرطها رو به ترتیب چک میکنه و به محض اینکه به اولین شرط true برسه، بلوک مربوط به اون رو اجرا میکنه و از کل ساختار if-else خارج میشه.
int score = 85;
char grade;
if (score >= 90)
{
grade = 'A';
}
else if (score >= 80)
{
// چون 85 >= 80 درست است، این بلوک اجرا میشه و تمام!
// دیگه شرطهای بعدی چک نمیشن.
grade = 'B';
}
else if (score >= 70)
{
grade = 'C';
}
else
{
grade = 'F';
}
Console.WriteLine($"Your grade is: {grade}"); // خروجی: Your grade is: B
🤔 حرف حساب و تجربه شما
این if-else ستون فقرات منطق در اکثر برنامههاست و تسلط بر اون برای نوشتن هر کدی ضروریه.
شما بیشتر برای چه سناریوهایی از if-else if-else های تودرتو استفاده میکنید؟ آیا جایگزین بهتری مثل switch (که بعداً بهش میرسیم) رو ترجیح میدید؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[پاتوق گیک های #C]
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #ControlFlow #IfElse #CodingTips
Forwarded from thisisnabi.dev [Farsi]
یه بعضی موقع ها اینقدر برنامه نویسی رو دوست دارم که توی ۴۸ ساعت، ۴۲ ساعت کد می نویسم.
بعضی موقع ها هم اینقدر حال به هم زن میشه که می خوام برم پیک موتوری کار کنم ول کنم همه چی رو :/
بعضی موقع ها هم اینقدر حال به هم زن میشه که می خوام برم پیک موتوری کار کنم ول کنم همه چی رو :/
💡 10 فرمان برای معماری تمیز در ASP.NET Core
کد تمیز مساوی است با یک سیستم مقیاسپذیر. این قوانین معماری رو دنبال کنید تا پروژهتون قابل نگهداری (maintainable) و توسعهدهندهپسند (developer-friendly) باقی بمونه.
1️⃣ از تزریق وابستگی (DI) همهجا استفاده کنید - هرگز سرویسهاتون رو با new نسازید.
2️⃣ کنترلرهاتون رو لاغر نگه دارید، منطق اصلی رو به لایه سرویس منتقل کنید.
3️⃣ مسئولیتها رو با استفاده از اینترفیسها و اصول SOLID جدا کنید.
4️⃣ برای جداسازی منطق خواندن/نوشتن، از الگوی CQRS استفاده کنید.
5️⃣ هرگز از داخل کنترلر مستقیماً به دیتابیس دسترسی پیدا نکنید - همیشه از طریق یک Repository یا سرویس این کار رو انجام بدید.
6️⃣ برای شکلدهی به دادهها بین لایهها، از DTO ها استفاده کنید - از ارسال مستقیم Entityهای دیتابیس به بیرون خودداری کنید.
7️⃣ اعتبارسنجی (Validation) رو با کتابخانههایی مثل FluentValidation یا DataAnnotations اضافه کنید - هرگز کورکورانه به دادههای ورودی اعتماد نکنید.
8️⃣ برای دغدغههای مشترک (cross-cutting concerns) مثل لاگینگ، مدیریت خطا و CORS، از Middleware ها استفاده کنید.
9️⃣ پروژهتون رو به لایهها ساختاربندی کنید: API، Application، Domain، Infrastructure.
🔟 برای سرویسهاتون Unit Test بنویسید - اگه تست کردن یه تیکه کد سخت باشه، اون کد یه بوی بدی میده (code smell)!
این قوانین رو دنبال کنید و اپلیکیشنهای .NET Core شما بعداً ازتون تشکر خواهند کرد. 😉
🤔 تلهی else سرگردان در #C:
به این قطعه کد نگاه کنید. به نظرتون else برای کدوم if اجرا میشه؟ اولی یا دومی؟
جواب این سوال میتونه باعث باگهای خیلی بدی تو برنامهتون بشه. بیاید این تله کلاسیک رو بررسی کنیم.
قانون #C (و خیلی از زبانهای دیگه) اینه: یک else همیشه به نزدیکترین if قبلی در همان بلوک کد که هنوز else نداره، متصل میشه.
پس کامپایلر، کد بالا رو در واقع اینجوری میبینه و تفسیر میکنه:
چون شرط if اول true هست، وارد بلوک داخلی میشیم. چون شرط if دوم false هست، else مربوط به همون if دوم اجرا میشه.
بهترین راه برای جلوگیری از این ابهام و نوشتن کد خوانا و بدون باگ، اینه که همیشه از براکت {} برای بدنهی if و else استفاده کنید، حتی اگه فقط یک خط کد دارید!
با براکت، شما صراحتاً و بدون هیچ ابهامی به کامپایلر (و بقیه توسعهدهندهها) میگید که منظورتون چیه.
حالا ببینید با جابجا کردن براکت، چطور منطق کد کاملاً عوض میشه:
تنها جایی که ننوشتن براکتهای اضافی کاملاً رایج و خواناست، الگوی معروف else if هست. با اینکه از نظر فنی این هم یه if تودرتوئه، ولی #C و IDEها این الگو رو به خوبی میشناسن و فرمت میکنن.
نوشتن براکتهای اضافی یه عادت سادهست که شما رو از ساعتها دیباگ کردن در آینده نجات میده.
شما جزو کدوم دستهاید؟ اونایی که همیشه از براکت استفاده میکنن یا اونایی که برای کدهای تکخطی حذفش میکنن؟ آیا تا حالا به خاطر همین موضوع به باگ خوردید؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[C# Geeks Hangout]
چرا براکت {} بهترین دوست شماست؟
به این قطعه کد نگاه کنید. به نظرتون else برای کدوم if اجرا میشه؟ اولی یا دومی؟
if (true)
if (false)
Console.WriteLine("Line 1");
else
Console.WriteLine("Line 2"); // این خط اجرا میشه! اما چرا؟
جواب این سوال میتونه باعث باگهای خیلی بدی تو برنامهتون بشه. بیاید این تله کلاسیک رو بررسی کنیم.
1️⃣ قانون else سرگردان (The Dangling Else Rule)
قانون #C (و خیلی از زبانهای دیگه) اینه: یک else همیشه به نزدیکترین if قبلی در همان بلوک کد که هنوز else نداره، متصل میشه.
پس کامپایلر، کد بالا رو در واقع اینجوری میبینه و تفسیر میکنه:
if (true)
{
// else به این if متصل شده
if (false)
Console.WriteLine("Line 1");
else
Console.WriteLine("Line 2");
}
چون شرط if اول true هست، وارد بلوک داخلی میشیم. چون شرط if دوم false هست، else مربوط به همون if دوم اجرا میشه.
2️⃣ راه حل: قدرت براکتها {} 🛡
بهترین راه برای جلوگیری از این ابهام و نوشتن کد خوانا و بدون باگ، اینه که همیشه از براکت {} برای بدنهی if و else استفاده کنید، حتی اگه فقط یک خط کد دارید!
با براکت، شما صراحتاً و بدون هیچ ابهامی به کامپایلر (و بقیه توسعهدهندهها) میگید که منظورتون چیه.
حالا ببینید با جابجا کردن براکت، چطور منطق کد کاملاً عوض میشه:
if (true)
{
if (false)
Console.WriteLine("Line 1");
}
else // حالا این else مال if اولی است!
{
Console.WriteLine("Line 2"); // و دیگر اجرا نمیشود.
}
3️⃣ تنها استثنای زیبا: الگوی else if ✨
تنها جایی که ننوشتن براکتهای اضافی کاملاً رایج و خواناست، الگوی معروف else if هست. با اینکه از نظر فنی این هم یه if تودرتوئه، ولی #C و IDEها این الگو رو به خوبی میشناسن و فرمت میکنن.
void TellMeWhatICanDo(int age)
{
if (age >= 35)
Console.WriteLine("You can be president!");
else if (age >= 21)
Console.WriteLine("You can drink!");
else if (age >= 18)
Console.WriteLine("You can vote!");
else
Console.WriteLine("You can wait!");
}
🤔حرف حساب و تجربه شما
نوشتن براکتهای اضافی یه عادت سادهست که شما رو از ساعتها دیباگ کردن در آینده نجات میده.
شما جزو کدوم دستهاید؟ اونایی که همیشه از براکت استفاده میکنن یا اونایی که برای کدهای تکخطی حذفش میکنن؟ آیا تا حالا به خاطر همین موضوع به باگ خوردید؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[C# Geeks Hangout]
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #CleanCode #IfElse #BestPractices
🔀 دستور switch:
از نوشتن if-else if-elseهای طولانی و تودرتو خسته شدید؟ برای وقتهایی که میخواید یه متغیر رو با چندین مقدار ثابت مقایسه کنید، #C یه راه حل خیلی تمیزتر و خواناتر داره: دستور switch.
ساختارش سادهست: شما یه متغیر رو به switch میدید و بعد با استفاده از case، حالتهای مختلفی که اون متغیر میتونه داشته باشه رو بررسی میکنید.
این switch کلاسیک دو تا ترفند جالب دیگه هم داره:
اگه میخواید برای چند تا case مختلف، یه کد یکسان اجرا بشه، میتونید اونها رو پشت سر هم و بدون break بنویسید:
یه قابلیت کمتر شناخته شده، goto case هست که به شما اجازه میده از یک case به یه case دیگه بپرید:
با اینکه switch در نسخههای جدید #C خیلی قدرتمندتر شده (که تو پست بعدی بهش میپردازیم)، همین نسخه کلاسیکش هم ابزار فوقالعادهای برای تمیزتر کردن منطقهای شرطی شماست.
شما بیشتر از switch استفاده میکنید یا if-else if؟ جایی بوده که switch واقعاً کدتون رو تمیزتر کرده باشه؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[پاتوق گیک های #C]
جایگزین تمیز برای if-elseهای تودرتو
از نوشتن if-else if-elseهای طولانی و تودرتو خسته شدید؟ برای وقتهایی که میخواید یه متغیر رو با چندین مقدار ثابت مقایسه کنید، #C یه راه حل خیلی تمیزتر و خواناتر داره: دستور switch.
1️⃣ ساختار اصلی switch
ساختارش سادهست: شما یه متغیر رو به switch میدید و بعد با استفاده از case، حالتهای مختلفی که اون متغیر میتونه داشته باشه رو بررسی میکنید.
ساختار case: مقدار ثابتی که میخوای بررسی کنی.
ساختار break: مهمترین بخش! بعد از اینکه کد یه case اجرا شد، break باعث میشه که از کل بلوک switch خارج بشیم. اگه break رو نذارید، کدتون خطا میده!
ساختار default: اگه هیچکدوم از caseها با مقدار متغیر نخوندن، این بلوک اجرا میشه. مثل else نهایی عمل میکنه.
void ShowCard(int cardNumber)
{
switch (cardNumber)
{
case 13:
Console.WriteLine("King");
break;
case 12:
Console.WriteLine("Queen");
break;
case 11:
Console.WriteLine("Jack");
break;
default: // برای بقیه اعداد
Console.WriteLine(cardNumber);
break;
}
}
2️⃣ تکنیکهای پیشرفتهتر switch کلاسیک
این switch کلاسیک دو تا ترفند جالب دیگه هم داره:
🧱 روی هم چیدن caseها:
اگه میخواید برای چند تا case مختلف، یه کد یکسان اجرا بشه، میتونید اونها رو پشت سر هم و بدون break بنویسید:
switch (cardNumber)
{
case 13:
case 12:
case 11:
Console.WriteLine("Face card");
break;
default:
Console.WriteLine("Plain card");
break;
}
⬆️پرش با goto case:
یه قابلیت کمتر شناخته شده، goto case هست که به شما اجازه میده از یک case به یه case دیگه بپرید:
switch (cardNumber)
{
case 12:
Console.WriteLine("Queen");
break;
case -1: // جوکر
goto case 12; // جوکر مثل بیبی حساب میشه!
// ... بقیه case ها
}
🤔 حرف حساب و تجربه شما
با اینکه switch در نسخههای جدید #C خیلی قدرتمندتر شده (که تو پست بعدی بهش میپردازیم)، همین نسخه کلاسیکش هم ابزار فوقالعادهای برای تمیزتر کردن منطقهای شرطی شماست.
شما بیشتر از switch استفاده میکنید یا if-else if؟ جایی بوده که switch واقعاً کدتون رو تمیزتر کرده باشه؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[پاتوق گیک های #C]
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #ControlFlow #SwitchCase #CleanCode
🚀 انقلاب switch در #C مدرن: Pattern Matching
تو پست قبلی، با switch کلاسیک آشنا شدیم. اما داستان اینجا تموم نمیشه! از #C 7 به بعد، switch یه تحول بزرگ داشته و به یه ابزار فوقالعاده قدرتمند به اسم Pattern Matching مجهز شده.
بیاید ببینیم این قابلیتهای جدید چی هستن و چطور switch رو از یه ابزار ساده، به یه غول تبدیل کردن.
1️⃣ سوییچ روی نوع (Switching on Type)
دیگه محدود به مقادیر ثابت نیستید! حالا میتونید یه متغیر از نوع object رو به switch بدید و بر اساس نوع واقعی اون در زمان اجرا، تصمیمگیری کنید و حتی همون لحظه اون رو تو یه متغیر جدید بریزید.
void TellMeTheType(object x)
{
switch (x)
{
// اگه x از نوع int بود، اون رو در متغیر i بریز و این بلوک رو اجرا کن
case int i:
Console.WriteLine($"It's an int! Square is {i * i}");
break;
// اگه از نوع string بود...
case string s:
Console.WriteLine($"It's a string! Length is {s.Length}");
break;
case null:
Console.WriteLine("It's null");
break;
default:
Console.WriteLine("I don't know what it is");
break;
}
}
2️⃣ اضافه کردن شرط با when
گاهی اوقات، فقط چک کردن نوع کافی نیست و میخواید یه شرط اضافه هم داشته باشید. کلمه کلیدی when به شما اجازه میده دقیقاً همین کار رو بکنید.
void ProcessShape(object shape)
{
switch (shape)
{
// این case فقط برای دایرههایی اجرا میشه که شعاعشون بزرگتر از 10 هست
case Circle c when c.Radius > 10:
Console.WriteLine("Big circle");
break;
case Circle c:
Console.WriteLine("Small or medium circle");
break;
case bool b when b == true: // فقط وقتی که بولین، true باشه
Console.WriteLine("It's true!");
break;
}
}
3️⃣ نکات تکمیلی و مهم
اهمیت ترتیب: برخلاف switch کلاسیک، در Pattern Matching ترتیب caseها مهمه! #C اونها رو از بالا به پایین چک میکنه و به محض رسیدن به اولین گزینهی منطبق، کار تموم میشه.
روی هم چیدن caseها: میتونید چند case مختلف رو برای اجرای یک کد مشترک، روی هم بچینید:
switch (x)
{
case float f when f > 1000:
case double d when d > 1000:
case decimal m when m > 1000:
Console.WriteLine("It's a large number!");
break;
}
🤔 حرف حساب و تجربه شما
این Pattern Matching، دستور switch رو از یه ابزار ساده به یه غول قدرتمند برای کار با دادهها و انواع مختلف تبدیل کرده.
شما از این قابلیتهای مدرن switch تو کدهاتون استفاده میکنید؟ کدوم ویژگیش (سوییچ روی نوع، when، ...) براتون کاربردیتر بوده؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[C# Geeks Hangout]
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #PatternMatching #SwitchCase #ModernCSharp
✨ از دستور به عبارت: تکامل switch در #C مدرن (Switch Expressions)
تو پستهای قبلی با switch کلاسیک و switch با Pattern Matching آشنا شدیم. اما انقلاب switch اینجا تموم نمیشه! از C# 8 به بعد، switch میتونه به عنوان یک عبارت (Expression) هم استفاده بشه.
یعنی چی؟ یعنی میتونه یه مقدار رو برگردونه! این قابلیت، کدهای شرطی رو به شدت کوتاهتر و خواناتر میکنه.
1️⃣ معرفی switch expression: خداحافظی با case و break
در این حالت جدید، switch دیگه یه دستور برای کنترل جریان نیست، بلکه یه عبارته که یه مقدار تولید میکنه. سینتکسش خیلی خلاصهتر و تمیزتره:
متغیر قبل از switch میاد.
به جای case x: و break;، از x => result, استفاده میکنیم.
برای حالت default، از _ (discard pattern) استفاده میکنیم.
string cardName = cardNumber switch
{
13 => "King",
12 => "Queen",
11 => "Jack",
_ => "Pip card" // معادل default
};
نکته مهم ⚠️: برخلاف switch statement، اگه تو یه switch expression حالت پیشفرض (_) رو نذارید و هیچکدوم از حالتها هم با مقدار ورودی نخونه، برنامهتون با یه Exception کرش میکنه!
2️⃣ قدرت اصلی: سوییچ روی چند مقدار با Tuple Pattern
قدرت واقعی switch expression وقتی مشخص میشه که میفهمید میتونید روی چند تا مقدار همزمان سوییچ کنید! این کار با استفاده از Tuples انجام میشه.
string cardName = (cardNumber, suite) switch
{
(13, "spades") => "King of spades",
(13, "clubs") => "King of clubs",
(12, "spades") => "Queen of spades",
// ...
_ => "Some other card"
};
این قابلیت به شما اجازه میده شرطهای خیلی پیچیده رو به شکلی فوقالعاده خوانا و تمیز بنویسید.
🤔 حرف حساب و تجربه شما
این switch expression یکی از بهترین قابلیتهای مدرن #C برای نوشتن کدهای تابعی (Functional)، خوانا و خلاصهست و در LINQ هم بسیار پرکاربرده.
شما از switch expression تو کدهاتون استفاده میکنید؟ به نظرتون این سینتکس از switch statement قدیمی بهتره؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدن ها و گپ و گفتهای خودمونی، فقط تو گروه.
[C# Geeks Hangout]
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #ModernCSharp #SwitchExpression #CleanCode
کدام کلمه کلیدی در #C برای جلوگیری از تغییر مقدار یک متغیر بعد از تعریف اولیه استفاده میشود؟
Anonymous Quiz
6%
static
13%
readonly
75%
const
6%
fixed
🔄 تکرار کد بدون تکرار! راهنمای کامل حلقهها (Loops) در #C
چطور میتونیم یه کار رو صد بار انجام بدیم، بدون اینکه صد خط کد بنویسیم؟ با استفاده از حلقهها (Loops)! حلقهها ستون فقرات هر الگوریتم پیچیدهای هستن و به ما اجازه میدن یک بلوک کد رو بارها و بارها تکرار کنیم.
امروز با ۴ نوع اصلی حلقه در #C آشنا میشیم.
1️⃣ حلقه while: تا وقتی که... ⏳
این سادهترین نوع حلقهست. یه شرط bool رو بررسی میکنه و تا وقتی اون شرط true باشه، بلوک کد رو تکرار میکنه.
نکته کلیدی: شرط قبل از هر بار اجرای بدنه حلقه چک میشه. پس اگه شرط از اول false باشه، بدنه حلقه هیچوقت اجرا نمیشه.
int i = 0;
while (i < 3)
{
Console.Write(i); // 012
i++;
}
2️⃣ حلقه do-while: حداقل یک بار اجرا کن!
این حلقه خیلی شبیه while هست، با این تفاوت مهم که شرط رو بعد از اجرای بلوک کد چک میکنه.
نتیجه مهم: این یعنی تضمین میشه که بدنه حلقه حداقل یک بار اجرا بشه، حتی اگه شرط از اول false باشه.
int i = 0;
do
{
Console.Write(i); // 012
i++;
}
while (i < 3);
3️⃣ حلقه for: حلقه همهکاره و قدرتمند 🔢
این حلقه برای تکرارهایی که تعدادشون مشخصه، عالیه. سه بخش اصلی و قدرتمند داره:
for (بخش مقداردهی اولیه; بخش شرط; بخش گام تکرار)
مقداردهی اولیه: فقط یک بار، قبل از شروع حلقه اجرا میشه (معمولاً برای تعریف شمارنده).
شرط: قبل از هر بار تکرار، این شرط bool چک میشه.
گام تکرار: بعد از هر بار اجرای بدنه حلقه، این بخش اجرا میشه (معمولاً برای افزایش شمارنده).
for (int i = 0; i < 3; i++)
{
Console.Write(i); // 012
}
مثال پیشرفته (دنباله فیبوناچی):
// محاسبه ۱۰ عدد اول دنباله فیبوناچی
for (int i = 0, prevFib = 1, curFib = 1; i < 10; i++)
{
Console.WriteLine(prevFib);
int newFib = prevFib + curFib;
prevFib = curFib;
curFib = newFib;
}
نکته حرفهای: شما میتونید هر کدوم از سه بخش for رو خالی بذارید. مثلاً for (;;) یه حلقه بینهایته!
4️⃣ حلقه foreach: پیمایش کلکسیونها به سبک مدرن ✨
این مدرنترین، خواناترین و امنترین راه برای حرکت روی تمام اعضای یک مجموعه قابل پیمایش (مثل آرایه یا string) هست. دیگه نگران ایندکس و خطای IndexOutOfRangeException نیستید!
// پیمایش روی تمام کاراکترهای رشته "beer"
foreach (char c in "beer")
{
Console.WriteLine(c);
}
// خروجی:
// b
// e
// e
// r
🤔 حرف حساب و تجربه شما
هر کدوم از این حلقهها ابزار مناسبی برای یه کار خاص هستن. انتخاب درست بینشون، کد شما رو تمیزتر و بهینهتر میکنه.
شما تو کدهاتون بیشتر از کدوم نوع حلقه استفاده میکنید؟ for یا foreach؟ سناریوی جالبی دارید که توش do-while بهترین انتخاب بوده باشه؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[C# Geeks Hangout]
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #ControlFlow #Loops #ForEach #ForLoop
🚀 پرشهای حرفهای در #C: راهنمای کامل break, continue, return, throw و goto
برنامه همیشه خط به خط اجرا نمیشه. گاهی وقتا لازمه از یه حلقه بپریم بیرون، یه تکرار رو رد کنیم، یا کل متد رو متوقف کنیم. #C برای این "پرش"ها، چند تا دستور کلیدی داره که بهشون میگیم Jump Statements.
بیاید با این ابزارهای قدرتمند برای کنترل جریان اجرا آشنا بشیم.
💡نکته مهم: تمام این دستورات پرش، از قوانین بلوک try...finally پیروی میکنن. یعنی اگه از داخل یه try به بیرون بپرید، بلوک finally همیشه قبل از پرش اجرا میشه.
1️⃣ break: فرار از حلقه!
دستور break مثل یه درِ خروج اضطراری برای حلقهها (for, while, ...) و switch عمل میکنه. به محض اجرا، حلقه یا switch رو فوراً متوقف میکنه و اجرا به اولین خط بعد از اون منتقل میشه.
int i = 0;
while (true) // حلقه بینهایت
{
if (i > 5)
break; // از حلقه فرار کن!
Console.Write(i + " ");
i++;
}
// خروجی: 0 1 2 3 4 5
// اجرا از اینجا ادامه پیدا میکنه
2️⃣ continue: این یکی رو بیخیال شو!
continue مثل دکمه "اسکیپ" برای تکرار فعلی حلقهست. بقیه کدهای داخل حلقه رو برای این تکرار نادیده میگیره و مستقیم میره سراغ شرط و گام تکرار بعدی.
for (int i = 0; i < 10; i++)
{
if ((i % 2) == 0) // اگه عدد زوجه
continue; // این تکرار رو بیخیال شو و برو سراغ i بعدی
Console.Write(i + " ");
}
// خروجی: 1 3 5 7 9
3️⃣ return: ماموریت انجام شد!
return به متد میگه "کار من اینجا تموم شد!" و اجرا رو به جایی که متد صدا زده شده بود، برمیگردونه. اگه متد خروجی داشته باشه (non-void)، return باید اون مقدار رو هم با خودش برگردونه.
decimal AsPercentage(decimal d)
{
decimal p = d * 100m;
return p; // با مقدار p به بیرون از متد برگرد
}
4️⃣ throw: اعلام وضعیت قرمز!
این throw برای پرتاب کردن یه Exception و اعلام وضعیت خطاست. وقتی یه throw اجرا میشه، اجرای عادی متوقف میشه و #C دنبال یه بلوک catch برای مدیریت اون خطا میگرده (که بعداً مفصل بهش میرسیم).
if (string.IsNullOrWhiteSpace(name))
{
throw new ArgumentNullException("Name cannot be null or empty!");
}
5️⃣ goto: شمشیر دولبه (و خطرناک!)
goto قدیمیترین و قدرتمندترین دستور پرشه، ولی استفاده ازش در ۹۹٪ مواقع توصیه نمیشه چون میتونه کد رو به "کد اسپاگتی" تبدیل کنه که دنبال کردنش خیلی سخته. با این حال، دونستنش برای شناخت کامل زبان خوبه. goto اجرا رو به یک لیبل (label) منتقل میکنه.
int i = 1;
startLoop: // این یک لیبل است
if (i <= 5)
{
Console.Write(i + " ");
i++;
goto startLoop; // به لیبل startLoop بپر
}
// خروجی: 1 2 3 4 5
🤔 حرف حساب و تجربه شما
تسلط بر این دستورات پرش، به شما کنترل کاملی روی جریان اجرای برنامهتون میده.
شما از کدوم یکی از این دستورات بیشتر استفاده میکنید؟ آیا تا حالا از goto تو یه سناریوی واقعی استفاده کردید که واقعاً لازم بوده باشه؟
خب، اینجا که نمیشه همه حرفا رو زد! 😉
ادامهی بحث، سوالات، غر زدنها و گپ و گفتهای خودمونی، فقط تو گروه.
[پاتوق گیک های #C]
🔖 هشتگها:
#CSharp #Programming #Developer #DotNet #ControlFlow #BestPractices #Exception
خروجی کد زیر چیست؟
int x = 5;
Console.WriteLine(++x);
int x = 5;
Console.WriteLine(++x);
Anonymous Quiz
45%
5
50%
6
5%
Compile Time Erorr
0%
4
📂 نظم و ترتیب در #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