در این پست یک برنامه بسیار ساده را ارائه می دهم که در آن با تغییر اندازه فرم ، یک برچسب با نوشتن ابعاد داخلی فرم همواره در وسط فرم قرار می گیرد . این کار نسبتا خیلی ساده است اما من از نوشتن این کد کوچک و ساده منظور خاصی را دنبال می کنم و آن هم این است که آنچه در برنامه نویسی از خود کدنویسی مهمتراست رعایت اصول برنامه نویسی می باشد که با رعایت آن نکات برنامه شما براحتی قابل توسعه خواهد بود یعنی تمام آنچه که شما در کدهای زیر می بینید را می شود در چند خط نیز نوشت اما اگر شما اصول برنامه نویسی را رعایت نکنید هنگامی که می خواهید برنامه خود را توسعه دهید و یا برای موارد مشابه استفاده کنید با مشکلات و چالش های بزرگی مواجه می شوید پس بهتر است از ابتدای کار بجای اینکه کد هدف را یاد بگیرید اصول استفاده صحیح از کدها را یاد گرفته و از آن مهمتر اینکه به رعایت این اصول عادت کنید در غیر این صورت شما از یک برنامه نویس که می تواند برنامه های چند خطی بنویسد پا فراتر نمی گذارید . حالا متن کد زیر را بدقت بررسی کنید.
using System;
using System.Drawing;
using System.Windows.Forms;
namespace DynamicLabelPositioner
{
public partial class Form1 : Form
{
private Label sizeInfoLabel;
public Form1()
{
InitializeComponent();
InitializeControls();
}
private void InitializeControls()
{
sizeInfoLabel = lbl; // فرض میکنیم lbl در طراحی فرم تعریف شده است
UpdateLabelText();
CenterControl(sizeInfoLabel);
}
private void Form1_SizeChanged(object sender, EventArgs e)
{
UpdateLabelText();
CenterControl(sizeInfoLabel);
}
/// بهروزرسانی متن برچسب با اطلاعات اندازه فرم
private void UpdateLabelText()
{
if (ClientSize.Width >= 0 && ClientSize.Height >= 0)
{
sizeInfoLabel.Text = $"Width: {ClientSize.Width} Height: {ClientSize.Height}";
}
else
{
sizeInfoLabel.Text = "Invalid size detected";
}
}
/// مرکزی کردن یک کنترل در فرم
private void CenterControl(Control control)
{
if (control == null) return;
int x = (ClientSize.Width - control.Width) / 2;
int y = (ClientSize.Height - control.Height) / 2;
/// اطمینان از مختصات مثبت
control.Location = new Point(Math.Max(0, x), Math.Max(0, y));
}
}
در برنامه نویسی شی گرا موارد و اصولی وجود دارد که باید رعایت شوند . من این اصول را در 16 مورد جمع آوری کرده ام که در ذیل با یک جمله کوتاه آنها را توضیح می دهم.
1 – Don't Repeat Yourself یعنی خودت را تکرار نکن : از نوشتن کد تکراری پرهیز کن
2 – Keep It Simple, Stupid یعنی ساده نگهش دار احمق : بدون فدا کردن کارایی و دقت کد آن را تا حد امکان ساده نگه دارید.
3 - You Aren’t Gonna Need It یعنی بهش نیاز نداری : فقط چیزی رو پیادهسازی کن که الان لازم داری .
4 - Testability یعنی قابلیت تستپذیری : کد طوری طراحی بشه که به راحتی بتونید براش تست بنویسید و رفتارش رو بررسی کنید .
5 - Resource Management یعنی مدیریت بهینه منابع سیستم (مثل حافظه، فایلها یا اتصالات) در برنامهنویسی، طوری که تخصیص و آزادسازی اونها به موقع و بدون نشتی (leak) انجام بشه، مثلاً با استفاده از using در سیشارپ برای بستن خودکار اتصالات.
6 - Portability یعنی قابلیت انتقالپذیری : کد شما باید بتونه روی سیستمها، پلتفرمها یا محیطهای مختلف (مثل ویندوز، لینوکس) بدون تغییر یا با کمترین تغییر اجرا بشه.
7 - Coding Standards یا استانداردهای کدنویسی به مجموعهای از قوانین و راهنماییها گفته میشه که برنامهنویسان برای نوشتن کد تمیز، خوانا و یکنواخت ازش پیروی میکنن تا همکاری تیمی، نگهداری و فهم کد آسونتر بشه.
8 - Security یعنی امنیت در برنامهنویسی، که به حفاظت از نرمافزار و دادهها در برابر دسترسی غیرمجاز، حملات یا سوءاستفاده اشاره داره، مثلاً با رمزنگاری یا اعتبارسنجی ورودیها.
9 - Performance Optimization یعنی بهینهسازی عملکرد، که به بهبود سرعت و کارایی برنامه با کاهش مصرف منابع (مثل CPU یا حافظه) از طریق تکنیکهایی مثل الگوریتم بهتر یا کش کردن اشاره داره.
10 - Internationalization (i18n) یعنی بینالمللیسازی، که به طراحی نرمافزار به شکلی اشاره داره که بتونه به راحتی با زبانها، فرهنگها و مناطق مختلف (مثل پشتیبانی از چند زبان یا فرمت تاریخ) سازگار بشه.
11 - Maintainability & Readability یعنی قابلیت نگهداری و خوانایی، که به نوشتن کدی اشاره داره که ساده، قابل فهم و منظم باشه تا توسعهدهندهها بتونن به راحتی اون رو درک کنن و تغییرش بدن.
12 - Single Responsibility Principle (SRP) یعنی اصل مسئولیت واحد، که میگه هر کلاس یا ماژول باید فقط یک وظیفه یا مسئولیت داشته باشه تا تغییرات سادهتر و کد منظمتر بشه.
13 - Open/Closed Principle (OCP) یعنی اصل باز/بسته، که میگه کلاسها باید برای گسترش (اضافه کردن قابلیت جدید) باز باشن، اما برای تغییر (ویرایش کد موجود) بسته باشن.
14 - Liskov Substitution Principle (LSP) یعنی اصل جانشینی لیسکوف، که میگه اشیاء از کلاسهای فرزند باید بتونن جای کلاس والد رو بگیرن بدون اینکه رفتار برنامه خراب بشه.
15 - Interface Segregation Principle (ISP) یعنی اصل تفکیک رابط، که میگه کلاسها نباید مجبور بشن رابطهایی رو پیادهسازی کنن که استفاده نمیکنن، و رابطها باید کوچک و خاص باشن.
16 - Dependency Inversion Principle (DIP) یعنی اصل وارونگی وابستگی، که میگه ماژولهای سطح بالا و پایین باید به abstraction (مثل رابطها) وابسته باشن، نه به پیادهسازیهای خاص.
مقدمه ساده برای درک مفهوم کلاس :
امروز میخوایم با یه چیز جالب توی برنامهنویسی سیشارپ آشنا بشیم:
کلاس : کلاس مثل یه نقشه یا طرح اولیهست که به ما کمک میکنه چیزهایی که توی برنامهمون نیاز داریم رو بسازیم. مثلاً فکر کنید یه کلاس مثل یه قالب کیکه؛ با اون قالب میتونید کلی کیک درست کنید که همشون شکلشون شبیه همه، ولی مواد داخلشون میتونه فرق داشته باشه. توی این درس، یه کلاس ساده درست میکنیم و یه کم راجع به اینکه چه جوری میشه بهش دسترسی داشت حرف میزنیم.
توضیح کلاس و سطوح دسترسی :
کلاس توی سیشارپ یه جور جعبهست که میتونیم توش اطلاعات (مثل اسم یا سن یا تاریخ و هر چیز دیگه) و یا کارهایی که میخوایم انجام بدیم (مثل سلام کردن یا محاسبه چند عبارت ریاضی و یا انجام یک کار روی فرم و هر کاری که می تونید تصور کنید) رو بذاریم. فرض کنید میخوایم یه کلاس برای "دانشآموز" درست کنیم. این کلاس میتونه اطلاعاتی مثل اسم و سن داشته باشه و کاری مثل گفتن "سلام" رو انجام بده. به این شکل:
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 باشه، فقط مدرسههایی که شعبه اون کتابخونهان میتونن به بخش ویژهش دسترسی داشته باشن.
مفهوم دلیگیت (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} رو توی فر میذاره. بوی خوبش داره میاد!");
}
}
خروجی این کد:
سفارش مشتری اول:
آشپز داره کباب کوبیده رو آماده میکنه. صبر کن داغ و تازه برسه!
سفارش مشتری دوم:
آشپز داره پیتزا مارگاریتا رو توی فر میذاره. بوی خوبش داره میاد!
سفارش مشتری سوم:
آشپز داره کباب و پیتزا با هم! رو آماده میکنه. صبر کن داغ و تازه برسه!
آشپز داره کباب و پیتزا با هم! رو توی فر میذاره. بوی خوبش داره میاد!