مقدمه ساده برای درک مفهوم کلاس :
امروز میخوایم با یه چیز جالب توی برنامهنویسی سیشارپ آشنا بشیم:
کلاس : کلاس مثل یه نقشه یا طرح اولیهست که به ما کمک میکنه چیزهایی که توی برنامهمون نیاز داریم رو بسازیم. مثلاً فکر کنید یه کلاس مثل یه قالب کیکه؛ با اون قالب میتونید کلی کیک درست کنید که همشون شکلشون شبیه همه، ولی مواد داخلشون میتونه فرق داشته باشه. توی این درس، یه کلاس ساده درست میکنیم و یه کم راجع به اینکه چه جوری میشه بهش دسترسی داشت حرف میزنیم.
توضیح کلاس و سطوح دسترسی :
کلاس توی سیشارپ یه جور جعبهست که میتونیم توش اطلاعات (مثل اسم یا سن یا تاریخ و هر چیز دیگه) و یا کارهایی که میخوایم انجام بدیم (مثل سلام کردن یا محاسبه چند عبارت ریاضی و یا انجام یک کار روی فرم و هر کاری که می تونید تصور کنید) رو بذاریم. فرض کنید میخوایم یه کلاس برای "دانشآموز" درست کنیم. این کلاس میتونه اطلاعاتی مثل اسم و سن داشته باشه و کاری مثل گفتن "سلام" رو انجام بده. به این شکل:
internal class Student
{
string name = " " علی ; یه اطلاعات //
int age = 15; // یه اطلاعات دیگه
void SayHello() // یه کار که کلاس میتونه بکنه
{
Console.WriteLine("سلام ، من " + name + " هستم! ");
}
}
اینجا Student یه کلاسه که نقشه ی یه دانشآموزه. حالا اگه بخوایم ازش استفاده کنیم، باید یه دانشآموز واقعی از روش بسازیم که بهش میگیم شیء یا Object
حالا بریم سراغ سطوح دسترسی. اینها مثل قفل در خونهاند که میگن کی میتونه وارد بشه و کی نمی تونه. توی سیشارپ، ما میتونیم با چند کلمه مشخص کنیم کلاسمون چه جوری دیده بشه. دو تا از مهمترینشون ایناس:
public اگه بنویسیم public class Student، یعنی همه میتونن از این کلاس استفاده کنن، حتی از جاهای دیگه توی برنامهمون. مثل اینه که در خونهتون همیشه باز باشه و هرکی بخواد بیاد داخل.
internal یعنی فقط کسایی که توی همون پروژه یا "خونه" هستن میتونن ازش استفاده کنن، و غریبهها نمیتونن بیان داخل. همآنطور که در مثال بالا می بینید کلاس با سطح دسترسی internal تعریف شده در چنین حالتی در صورتی که مایل باشید می توانید از نوشتن این کلمه صرفنظر کنید زیرا سی شارپ بطور پیشفرض کلاسی را که برایش سطح دسترسی تعیین نشده آن را internal در نظر می گیرد .
یه مثال سادهتر: فکر کنید کلاس Student مثل یه کتابخونهست که داخل کلاس ماست اگه public باشه، همه دانشآموزای مدرسه میتونن بیان و کتاب بگیرن. اما اگه internal باشه، فقط بچههای کلاس خودمون میتونن ازش استفاده کنن. اگه بخوایم چیزایی داخل کلاس (مثل اسم یا سن) رو قفل کنیم که فقط خود کلاس بتونه باهاشون کار کنه، از private استفاده میکنیم.
بیائید مثال کتابخونه رو کمی بیشتر باز کنیم و تا جایی که امکان دارد سطوح دسترسی را با این مثال توضیح دهیم.
فکر کنید یه کتابخونه داریم که پر از کتابهای مختلفه. این کتابخونه مثل یه کلاس توی سیشارپه که میتونه چیزایی مثل کتاب (اطلاعات) و کارایی مثل امانت دادن (متدها) رو داشته باشه . حالا اینکه کی بتونه بیاد از این کتابخونه استفاده کنه، بستگی داره به اینکه درش رو چه جوری قفل کنیم. توی سیشارپ به این قفلها میگیم سطوح دسترسی. بیاید با مثال کتابخونه ببینیم این سطوح چیان!
فرض کنیم کتابخونهمون یه اسم داره، مثلاً Library، و توی یه پروژه سیشارپ تعریفش کردیم. حالا میخوایم ببینیم چه کسایی میتونن بیان داخلش:
public عمومی اگه بگیم public class Library, یعنی در کتابخونه برای همه بازه! هر دانشآموزی از هر مدرسهای توی شهر، حتی اگه از یه جای دیگه باشه، میتونه بیاد و کتاب بگیره. مثلاً:
public class Library
{
string book = "داستانهای خوب";
void LendBook()
{
Console.WriteLine("کتاب امانت داده شد!");
}
}
اینجا همه میتونن از کتابخونه استفاده کنن، حتی اگه توی یه پروژه دیگه باشن (مثلاً یه برنامه جداگانه).
internal یا داخلی : اگه چیزی ننویسیم، مثل class Library, سیشارپ خودش اون رو internal میکنه. یعنی فقط کسایی که توی همون مدرسه (یا همون پروژه) هستن میتونن بیان داخل. مثلاً:
class Library
{
string book = "ریاضی آسون";
}
توی این حالت، فقط دانشآموزای مدرسهای که کتابخونه توش ساخته شده میتونن ازش استفاده کنن. اگه یه مدرسه دیگه بخواد بیاد، در قفله و نمیتونه!
private خصوصی : حالا اگه بخوایم چیزایی توی کتابخونه رو قفل کنیم (مثل کتابها یا کارهاش)، از private استفاده میکنیم.
توجه کنید که این سطح برای خود کلاس نیست، بلکه برای چیزای داخلش (مثل متدها یا متغیرها) استفاده میشه. مثلاً:
public class Library
{
private string secretBook = "گنج مخفی"; // فقط خود کتابخونه میتونه ببینه
public string normalBook = "داستان ساده"; // همه میتونن ببینن
}
اینجا secretBook مثل یه کتاب توی گاوصندوق کتابخونهست که فقط مسئول کتابخونه (یعنی خود کلاس) میتونه بهش دست بزنه، ولی normalBook برای همه آزاده.
protected محافظتشده
این یه کم خاصتره. فرض کنید کتابخونه یه بخش ویژه داره که فقط برای کسایی که از خانواده کتابخونه باشن (مثلاً کلاسهای فرزند) بازه. مثلاً:
public class Library
{
protected string familyBook = "کتاب خانوادگی";
}
public class SpecialLibrary : Library
{
void CheckBook()
{
Console.WriteLine(familyBook); // فقط اینجا میتونه ببینه
}
}
اینجا familyBook فقط برای کلاسهایی که از Library ارث میبرن مثل SpecialLibrary قابل دسترسیه، نه برای بقیه.
جمعبندی :
اگه کتابخونه public باشه، همه بچههای شهر میتونن بیان و کتاب بگیرن.
اگه internal باشه، فقط بچههای همون مدرسهای که کتابخونه توشه میتونن بیان.
اگه یه کتاب داخلش private باشه، فقط مسئول کتابخونه میتونه بهش دست بزنه.
اگه protected باشه، فقط مدرسههایی که شعبه اون کتابخونهان میتونن به بخش ویژهش دسترسی داشته باشن.
در سی شارپ دو دسته ساختار کنترلی بسیار مفید و پرکاربرد وجود دارد
دستهی اول: حلقهها (Loops)
شامل :
for
foreach
while
do-while
دستهی دوم: ساختارهای تصمیمگیری (Decision-Making Structures)
شامل :
If
If – else
If – else if – else
Switch – case
هنگام استفاده از ساختارهای کنترلی بالا گاهی اوقات می توانیم از "دستورات کنترل جریان" (Control Flow Statements) که شامل موارد زیر می باشند نیز استفاده کنیم .
break : به این دستور گاهی «دستور قطع» یا «Break Statement» میگویند، زیرا اجرای حلقه یا ساختار کنترلی مثل switch-case را به طور کامل متوقف کرده و کنترل را به خارج از آن منتقل میکند.
continue : به این دستور معمولاً «دستور ادامه» یا «Continue Statement» میگویند، زیرا باعث میشود اجرای دور فعلی حلقه متوقف شود و مستقیماً به دور بعدی حلقه برود.
در ادامه با 10 مثال انواع مختلف ساختارهای کنترلی را بیان می کنیم .
۱. ساختار شرطی (if) : برای اجرای کد در صورت برقراری یک شرط.
int age = 6;
if (age >= 6)
{
Console.WriteLine("شما مجاز به ثبت نام در دبستان هستید.");
}
۲. ساختار شرطی (if-else) : برای اجرای یک بلوک کد در صورت برقراری شرط و بلوک دیگر در صورت عدم برقراری شرط.
int age = 16;
if (age >= 18)
{
Console.WriteLine("شما مجاز به رای دادن هستید.");
}
else
{
Console.WriteLine("شما مجاز به رای دادن نیستید.");
}
۳. ساختار شرطی (if-else if-else) : برای بررسی چندین شرط.
int score = 85;
if (score >= 90)
{
Console.WriteLine("نمره شما A است.");
}
else if (score >= 80)
{
Console.WriteLine("نمره شما B است.");
}
else
{
Console.WriteLine("نمره شما C است.");
}
۴. ساختار تکرار (for) : برای تکرار یک بلوک کد به تعداد مشخصی.
for (int i = 0; i < 5; i++)
{
Console.WriteLine("شمارش: " + i);
}
۵. ساختار تکرار (while) : برای تکرار یک بلوک کد تا زمانی که یک شرط برقرار باشد.
int count = 0;
while (count < 5)
{
Console.WriteLine("شمارش: " + count);
count++;
}
۶. ساختار تکرار (do-while) : برای اجرای یک بلوک کد حداقل یک بار و سپس تکرار آن تا زمانی که شرط برقرار باشد.
int count = 0;
do
{
Console.WriteLine("شمارش: " + count);
count++;
} while (count < 5);
۷. ساختار کنترل (break) : برای خروج از حلقههای تکرار.
for (int i = 0; i < 10; i++)
{
if (i == 5)
{
break;
}
Console.WriteLine(i);
}
۸. ساختار کنترل (continue) : برای رد شدن از بقیه کد در یک تکرار و رفتن به تکرار بعدی.
for (int i = 0; i < 5; i++)
{
if (i == 2)
{
continue;
}
Console.WriteLine(i);
}
۹. ساختار کنترل (switch) : برای انتخاب یکی از چندین بلوک کد بر اساس مقدار یک متغیر.
int day = 3;
switch (day)
{
case 1:
Console.WriteLine("شنبه");
break;
case 2:
Console.WriteLine("یکشنبه");
break;
case 3:
Console.WriteLine("دوشنبه");
break;
default:
Console.WriteLine("روز نامعتبر");
break;
}
۱۰. ساختار کنترل (return) : برای بازگشت از یک تابع و خاتمه اجرای آن.
int Add(int a, int b)
{
return a + b;
}
int result = Add(5, 3);
Console.WriteLine("نتیجه: " + result);
این مثالها مفاهیم پایهای ساختارهای کنترلی در سیشارپ را نشان میدهند.
مفهوم دلیگیت (Delegate) در سیشارپ به زبانی ساده :
با درود امروز میخواهیم با یه مفهوم کاربردی در سیشارپ آشنا بشیم که اسمش دلیگیت (Delegate) هست. فکر کنید دلیگیت مثل یه "پیک موتوری" توی دنیای برنامهنویسیه که کارش اینه که تابع رو براتون ببره و هرجا که گفتید تحویل بده! این پیک موتوری فقط همون کاری رو انجام میده که شما بهش سفارش دادید، نه بیشتر و نه کمتر. بیاید این مفهوم رو با یه داستان یاد بگیریم.
تصور کنید توی یه رستوران کار میکنید. مشتریها سفارش غذا میدن (مثلاً کباب یا پیتزا)، اما شما بهعنوان آشپز نمیتونید خودتون غذا رو ببرید سر میز. اینجا دلیگیت وارد میشه! دلیگیت مثل گارسون رستوران عمل میکنه: شما بهش میگید "این تابع پخت کباب رو بگیر و هر وقت مشتری صداش کرد، براش اجرا کن". حالا هر مشتری که سفارش کباب بده، دلیگیت میدونه باید کدوم تابع رو صدا بزنه. این یعنی دلیگیت یه جور "واسطه" بین سفارشدهنده (رویداد) و اجراکننده (تابع) می باشد.
از نظر فنی، دلیگیت یه نوع داده (Type) خاص تو سیشارپه که میتونه به یه تابع اشاره کنه. مثل یه اشارهگر (Pointer) عمل میکنه، ولی خیلی امنتر و شیکتره. شما اول دلیگیت رو تعریف میکنید و میگید که چه شکلی تابعها رو قبول میکنه (مثلاً ورودی و خروجی تابع چی باشه)، بعد هر تابعی که این شکل رو داشته باشه میتونه به دلیگیت وصل بشه. این قابلیت باعث میشه کدتون انعطافپذیر بشه، چون میتونید تابعها رو مثل لِگو به جاهای مختلف وصل کنید و جدا کنید!
به مثال رستوران برمی گردیم فرض کنید توی رستوران دو نوع غذا داریم: کباب و پیتزا. میخواهیم یه دلیگیت بسازیم که سفارش مشتری رو به آشپز برسونه:
using System;
class Program
{
// تعریف دلیگیت: یه پیک موتوری که فقط اسم غذا رو میبره و تحویل میده
delegate void FoodOrder(string foodName);
static void Main(string[] args)
{
// ساختن یه دلیگیت به اسم waiter (گارسون)
FoodOrder waiter;
// وصل کردن دلیگیت به تابع پخت کباب
waiter = CookKebab;
Console.WriteLine("سفارش مشتری اول:");
waiter("کباب کوبیده"); // گارسون میره کباب رو سفارش میده
// حالا وصل کردن دلیگیت به تابع پخت پیتزا
waiter = CookPizza;
Console.WriteLine("سفارش مشتری دوم:");
waiter("پیتزا مارگاریتا"); // گارسون حالا پیتزا رو سفارش میده
// میتونیم چند تابع رو با هم وصل کنیم (Multicast)
waiter = CookKebab;
waiter += CookPizza; // گارسون حالا دوتا کار رو با هم انجام میده
Console.WriteLine("سفارش مشتری سوم:");
waiter("کباب و پیتزا با هم!");
}
// تابع پخت کباب
static void CookKebab(string foodName)
{
Console.WriteLine($"آشپز داره {foodName} رو آماده میکنه. صبر کن داغ و تازه برسه!");
}
// تابع پخت پیتزا
static void CookPizza(string foodName)
{
Console.WriteLine($"آشپز داره {foodName} رو توی فر میذاره. بوی خوبش داره میاد!");
}
}
خروجی این کد:
سفارش مشتری اول:
آشپز داره کباب کوبیده رو آماده میکنه. صبر کن داغ و تازه برسه!
سفارش مشتری دوم:
آشپز داره پیتزا مارگاریتا رو توی فر میذاره. بوی خوبش داره میاد!
سفارش مشتری سوم:
آشپز داره کباب و پیتزا با هم! رو آماده میکنه. صبر کن داغ و تازه برسه!
آشپز داره کباب و پیتزا با هم! رو توی فر میذاره. بوی خوبش داره میاد!
در این پست مفاهیم اصلی برنامه نویسی شی گرا رو نام برده و بطور ساده بیان می کنیم که هر کدام در چه شرایطی می تونن به ما کمک کنن تا بتونیم کاری رو که به نظر غیر ممکن میاد رو انجام بدیم.
با C#، شما میتونید تقریباً هر چیزی که تو ذهنتون هست رو به واقعیت تبدیل کنید. از ساختن برنامههای ساده مثل ماشینحساب گرفته تا طراحی بازیهای پیچیده، وبسایتها، اپلیکیشنهای موبایل، سیستمهای مدیریت بانک، یا حتی ابزارهایی که با هوش مصنوعی کار میکنن. فرض کنید میخواهید یه برنامه بسازید که اطلاعات دانشجوها رو مدیریت کنه، یا یه ربات که توی یه بازی خودش تصمیم بگیره کجا بره. این کارها با استفاده از مفاهیم شیگرایی خیلی منظمتر و قابلفهمتر میشن. ببینیم هر کدوم از این مفاهیم چطور به ما کمک میکنن تا این کارها رو انجام بدیم.
1 . کپسولهسازی (Encapsulation): اگه بخواهیم یه چیزی رو مخفی کنیم و فقط به شکلی که خودمون میخواهیم در دسترس بذاریم، از کپسولهسازی استفاده میکنیم. مثلاً تو برنامه مدیریت دانشجوها، فرض کنید نمیخواهیم کسی بتونه مستقیماً نمره یه دانشجو رو تغییر بده و فقط از طریق یه متد خاص با شرط (مثلاً نمره بین 0 تا 20) این کار رو بکنه. اینجا با تعریف فیلدهای خصوصی (مثل private int score) و پراپرتیها با get و set، این کنترل رو به دست میگیریم. این کار باعث میشه کد امنتر بشه و جلوی اشتباهات رو بگیره.
2 . ارثبری (Inheritance): حالا اگه بخواهیم یه کار بهظاهر غیرممکن مثل مدیریت همزمان دانشجوها، استادها و کارمندها رو تو یه سیستم انجام بدیم، از ارثبری استفاده میکنیم. مثلاً یه کلاس پایه به اسم Person میسازیم که ویژگیهای مشترک مثل اسم و کد ملی رو داره. بعد کلاسهای Student، Professor و Employee ازش ارث میبرن و هر کدوم ویژگیهای خاص خودشون (مثل نمره برای دانشجو یا حقوق برای کارمند) رو اضافه میکنن. اینجوری بدون اینکه کد تکراری بنویسیم، همه رو با یه ساختار منظم مدیریت میکنیم.
3 . چندریختی (Polymorphism): فرض کنید میخواهیم یه کار عجیب بکنیم: یه تابع بنویسیم که بتونه برای هر نوع موجود (دانشجو، استاد، کارمند) یه پیام خاص چاپ کنه، بدون اینکه برای هر کدوم یه تابع جدا بنویسیم. اینجا چندریختی به کمکمون میاد. با تعریف یه متد مجازی (virtual) تو کلاس Person مثل PrintRole() و اورراید کردنش تو کلاسهای فرزند، هر شیء خودش تصمیم میگیره چه پیامی نشون بده. مثلاً student.PrintRole() میگه "من دانشجوام" و professor.PrintRole() میگه "من استادیارم". این انعطافپذیری واقعاً شگفتانگیزه!
4 . انتزاع (Abstraction): اگه بخواهیم پیچیدگیها رو کم کنیم و فقط چیزای مهم رو نشون بدیم، از انتزاع استفاده میکنیم. مثلاً فرض کنید میخواهیم یه سیستم پرداخت آنلاین بسازیم که هم با کارت بانکی کار کنه هم با کیف پول دیجیتال. یه کلاس انتزاعی یا اینترفیس به اسم IPayment تعریف میکنیم که فقط متد Pay() رو مشخص میکنه. حالا هر نوع پرداخت (مثل CreditCardPayment یا DigitalWalletPayment) خودش جزئیاتش رو پیادهسازی میکنه، و ما فقط با Pay() کارمون رو پیش میبریم بدون اینکه درگیر پیچیدگیها بشیم.
6 - 5 . کلاسها و اشیا (Classes and Objects): پایه همه این کارها کلاسها و اشیائند. مثلاً میخواهیم یه بازی بسازیم که توش شخصیتها، سلاحها و دشمنها باشن. برای هر کدوم یه کلاس تعریف میکنیم (مثل Player، Weapon، Enemy) و با ساختن اشیا از این کلاسها، دنیای بازیمون رو زنده میکنیم. هر شیء میتونه ویژگیها و رفتارهای خاص خودش رو داشته باشه، مثلاً یه بازیکن با قدرت 50 و یه سلاح با Damage 20.
7 . پیامرسانی (Messaging): این مفهوم وقتی به کار میاد که بخواهیم اشیا با هم حرف بزنن. مثلاً تو همون بازی، اگه بازیکن به دشمن حمله کنه، شیء Player یه پیام (فراخوانی متد) به شیء Enemy میفرسته که مثلاً TakeDamage(20) رو اجرا کنه. اینجوری اشیا با هم تعامل میکنن و کارای پیچیده مثل نبرد رو ممکن میکنن.
8 . ارتباط و وابستگی (Association/Dependency): اگه بخواهیم یه سیستم بزرگتر بسازیم که بخشهاش به هم وصل باشن، از این مفهوم استفاده میکنیم. مثلاً تو یه برنامه فروشگاهی، کلاس Order به کلاس Customer وابستهست چون هر سفارش مال یه مشتریه. یا تو بازی، Player به Weapon وابستهست چون بازیکن بدون سلاح نمیتونه حمله کنه. این ارتباطها باعث میشه سیستم ما مثل یه پازل کامل کار کنه.
9 . اینترفیس (Interface): اگه بخواهیم یه کار بهظاهر غیرممکن مثل وصل کردن بخشهای مختلف برنامه رو بدون اینکه به جزئیاتشون گیر کنیم انجام بدیم، از اینترفیس استفاده میکنیم. فرض کنید میخواهیم یه سیستم پخش موسیقی بسازیم که هم با بلندگوهای بلوتوثی کار کنه، هم با هدفون سیمی، هم با یه دستگاه پخش ماشین. یه اینترفیس به اسم IPlayable تعریف میکنیم که فقط متد Play() رو مشخص کنه. حالا هر دستگاه (مثل BluetoothSpeaker یا WiredHeadphones) این اینترفیس رو پیادهسازی میکنه و خودش تصمیم میگیره چطور صدا رو پخش کنه. اینجوری کد ما انعطافپذیر میشه و میتونیم بدون تغییر برنامه اصلی، دستگاههای جدید رو بهش اضافه کنیم. این قدرت انتزاع و استانداردسازی واقعاً ذهن آدم رو درگیر میکنه که چطور یه ایده ساده این همه کار رو ساده میکنه!
10 . سطوح دسترسی (Access Modifiers): حالا اگه بخواهیم یه سیستم امن و منظم بسازیم که هرکسی نتونه به هر بخشش دست بزنه، از سطوح دسترسی مثل public، private، protected و internal استفاده میکنیم. مثلاً تو یه برنامه بانکی، میخواهیم موجودی حساب مشتری فقط از طریق یه متد خاص (مثل برداشت یا واریز) تغییر کنه و کسی مستقیم بهش دسترسی نداشته باشه. با تعریف فیلد private decimal balance و یه متد عمومی مثل public void Withdraw(decimal amount) که شرطهای لازم (مثل کافی بودن موجودی) رو چک کنه، این کار رو ممکن میکنیم. یا مثلاً تو یه پروژه تیمی، با internal یه کلاس رو فقط برای همون اسمبلی نگه میداریم تا بقیه پروژهها بهش دست نزنن. این کنترل دسترسی باعث میشه کد ما هم قابلاعتمادتر بشه، هم ساختارش شفافتر بمونه.
با این مفاهیم، شما میتونید کارهایی بکنید که شاید اولش غیرممکن به نظر بیاد. مثلاً یه برنامه بسازید که خودش تصمیم بگیره کی تخفیف بده، یا یه رابط کاربری که با یه کلیک کلی کار انجام بده. C# و شیگرایی بهتون این قدرت رو میدن که ایدههاتون رو به شکلی منظم و قابلمدیریت به کد تبدیل کنید. هر کدوم از این مفاهیم مثل یه ابزار توی جعبهابزار شماست که بسته به نیازتون میتونید ازش استفاده کنید تا دنیای دیجیتال رو اونجوری که میخواهید بسازید!