برنامه نویسی شی گرا (OOP) Object-Oriented Programming
مفهوم کلاس (Class)
کلاس یک مفهوم پایهای بسیار مهم در برنامهنویسی شی گراست. کلاس یک بسته (blueprint) است که شامل تعدادی از ویژگیها (fields) و صفاتی است که یک شی میتواند داشته باشد و همچنین شامل تواناییها (methods)یی است که یک شی قادر به انجام آنهاست.
کلاس همچون یک قالب، نقشه و یا الگو عمل میکند و ویژگیهای عمومی اشیاء را تعیین میکند. کلاس به صورت مستقیم قابل استفاده نیست و باید از آن یک شیء (object) یا همان نمونه (instance) ساخت تا با کمک شیء بتوان از ویژگیها و قابلیتهای درون کلاس استفاده کرد.
در تعریف بالا اسم شی را بردیم؛ شیء چیست؟ شیء (Object) یک نمونه (instance) از کلاس است که هنگام استفاده از یک کلاس باید ابتدا یک شیء از آن کلاس را نمونهسازی (instantiate) کنیم.
( البته بعضی از کلاسها را نمیتوان نمونهسازی کرد که بعدا توضیح خواهیم داد )
حالا برویم سراغ ساخت یک کلاس. فرض کنید ما کلاسی میخواهیم داشته باشیم که تمام اعضای این کلاس باید یک نام و یک سن داشته باشند و بتوانند خودشان را معرفی کنند. همانطور که میبینید کلاس زیر این دو ویژگی و یک توانایی را دارد:
class Student
{
// (Fields) ویژگی ها و دادههایی که شیء نگهداری میکند
public string Name; // نام دانشجو
public int Age; // سن دانشجو
// متد سازنده (Constructor) - متدی خاص که هنگام ایجاد شیء اجرا میشود و ویژگیها را مقداردهی اولیه میکند
public Student(string name, int age)
{
Name = name;
Age = age;
}
// متد (Method) - رفتارهایی که شیء میتواند انجام دهد
public void Introduce()
{
Console.WriteLine($"سلام، من {Name} هستم و {Age} سال دارم.");
}
}
خب در کلاس Student میبینید که دو ویژگی به نامهای Name و Age وجود دارد. Name از نوع string است و برای ذخیره نام دانشجو استفاده میشود. Age از نوع int است و برای ذخیره سن دانشجو استفاده میشود. همچنین یک متد سازنده (Constructor) وجود دارد که این ویژگیها را مقداردهی اولیه میکند (این متد هنگام ایجاد شیء اجرا میشود). این کلاس همچنین دارای یک متد به نام Introduce میباشد که قادر است یک متن را در کنسول چاپ کند.
در بالا گفتیم که هنگام استفاده از یک کلاس، لازم است تا یک نمونه از آن را بسازیم (نمونهسازی کنیم). در دو خط زیر، ما دو نمونه از کلاس Student را با نامهای student1 و student2 ساخته و مقداردهی اولیه کردهایم:
// ساخت شیء از کلاس Student (نمونهسازی)
Student student1 = new Student("رضا", 20);
Student student2 = new Student("الهام", 22);
در واقع با ایجاد نمونه از کلاس، یک فضای حافظه برای شیء اختصاص داده میشود و ویژگیهای آن با مقادیر اولیه مقداردهی میشوند. با ایجاد نمونه از کلاس به تنهایی کار خاصی انجام نمیشود و این نمونه درون حافظه بصورت بلااستفاده میماند. ما باید کاری کنیم تا هدف اصلی از موجودیت کلاس تامین شود و آن هم اجرای قابلیت های درون کلاس است و آن هم با کمک گرفتن از نام نمونه میسر می باشد
در دو خط زیر، با کمک نام نمونههای ساخته شده از کلاس بالا، به تنها تابع موجود در آن دسترسی پیدا کرده و آن را به اجرا درمیآوریم:
// فراخوانی متد برای هر شیء
student1.Introduce(); // خروجی: سلام، من رضا هستم و 20 سال دارم.
student2.Introduce(); // خروجی: سلام، من الهام هستم و 22 سال دارم.
در این درس آموختید چگونه یک کلاس را تعریف کنید، ویژگیها و رفتارهای مورد نیاز را در آن قرار دهید، و با نمونهسازی از آن ، کلاس را به خدمت بگیرید. تا درس بعدی، خدا نگهدار
معرفی کتابخانه AdibanDefDateAndTime: راهحلی ساده برای محاسبه اختلاف بین دو تاریخ!
آیا تا حالا توی پروژههاتون نیاز داشتید اختلاف بین یه تاریخ خاص و تاریخ فعلی سیستم رو محاسبه کنید؟ یا دنبال راهی هستید که این کار رو بدون درگیر شدن با پیچیدگیهای تاریخ و زمان انجام بدید؟ ما براتون یه راهحل ساده و کاربردی داریم: کتابخانه AdibanDefDateAndTime.
این کتابخانه یه کلاس قدرتمند به اسم DateTimeComparer داره که بهتون کمک میکنه بهراحتی اختلاف بین یه تاریخ مشخص (بهصورت رشته) و تاریخ فعلی سیستم رو محاسبه کنید. خروجی هم بهصورت یه رشته خوانا و قابلفهم برگردونده میشه.
استفاده ازش خیلی راحته! فقط کافیه این مراحل رو دنبال کنید:
مرحله 1: اضافه کردن کتابخانه به پروژه
اول فایل DLL کتابخانه AdibanDefDateAndTime رو دانلود کنید و به پروژهتون اضافه کنید:
مرحله 2: اضافه کردن فضای نام (Namespace)
توی کدتون، این خط رو بالای فایل بنویسید:
مرحله 3: استفاده از تابع CompareDateTime
حالا میتونید از کلاس DateTimeComparer و تابع CompareDateTime اینجوری استفاده کنید:
{ label1.Text = DateTimeComparer.CompareDateTime("2025-02-07/16:00:00");}یا اگه بخواید اختلاف رو هر ثانیه بهروز کنید (مثلاً وقتی کمتر از 24 ساعت مونده)، میتونید از تایمر استفاده کنید:
{ label1.Text = DateTimeComparer.CompareDateTime("2025-02-07/02:21:00");}ما این کتابخانه رو بهصورت رایگان در اختیارتون گذاشتیم تا توی پروژههاتون ازش استفاده کنید. برای دانلود فایل DLL، روی تصویر زیر کلیک کنید:
پس از اینکه در درس قبلی با مفهوم کلاس و شیء آشنا شدید، در این جلسه میخواهم درباره اولین رکن از چهار رکن اصلی برنامهنویسی شیءگرا توضیحاتی بدم.
کپسولهسازی یا Encapsulation:
کپسولهسازی به معنی بستهبندی دادهها (ویژگیها) و رفتارها (متدها) توی یه
کلاس و کنترل دسترسی به اونهاست. فکر کنید مثل یه کپسول دارویی که مواد داخلش رو
محافظت میکنه و فقط از راه مشخصی (مثل خوردن) میشه بهش دسترسی داشت. یا باتری
ماشین که فقط با اتصال دو سیم میتونید ازش استفاده کنید، یا خود ماشین که با کمک
سوئیچ روشن میشه و هزاران مثال دیگه.
هدف Encapsulation در سیشارپ اینه که
دادهها رو از دسترسی یا تغییرات مستقیم و ناخواسته محافظت کنه، جزئیات داخلی
پیادهسازی رو مخفی نگه داره و دسترسی به دادهها رو فقط از طریق متدهای مشخص و
کنترلشده (مثل خواص یا متدها) فراهم کنه.
در واقع بدون کپسولهسازی، هر بخشی از کد (مثل متدها یا اشیاء دیگه) میتونه
مستقیماً به دادههای داخلی کلاس دسترسی داشته باشه و اونا رو تغییر بده. این
مسئله میتونه منجر به بروز خطاها یا رفتارهای غیرقابل پیشبینی در برنامه بشه، بهخصوص
توی پروژههای بزرگ و پیچیده که چندین توسعهدهنده روی کد کار میکنن. این اولین
قدم برای ساختن کد امن و قابل نگهداریه.
برای درک مفهوم کپسولهسازی فقط کافیه تا با مفهوم متغیرها و همچنین دو
مفهوم کلاس و شیء آشنا باشید که دو مورد آخر توی درس قبلی معرفی شدن.
چه مواقعی از کپسولهسازی در پروژه خود استفاده کنیم؟
کپسولهسازی را چگونه در پروژه خود
اعمال کنیم؟
در واقع برای این کار ابتدا از سطوح دسترسی مناسب برای جلوگیری از در دسترس
بودن برخی فیلدها و سپس استفاده از پراپرتیهای
(get/set)
برای خواندن و تغییر این فیلدها و همچنین استفاده از متدها برای تغییر یا
مشاهده قاعدهمند دادهها استفاده میکنیم. این یعنی اینکه ما دسترسی به فیلدها رو
محدود و طبق شرایط خاص مجاز کردیم.
مثال عملی: حساب بانکی
فرض کنید یه کلاس برای حساب بانکی داریم. میخواهیم موجودی حساب رو کنترل
کنیم تا مستقیم تغییر نکنه و منفی هم نشه. اینجوری کپسولهسازی پیاده سازی میشه:
public class BankAccount
{
private decimal _balance; // موجودی حساب مخفی شده
public decimal Balance // پراپرتی برای دسترسی کنترلشده
{
get { return _balance; }
set
{
if (value >= 0) // قانون: موجودی نمیتونه منفی باشه
_balance = value;
}
}
public void Deposit(decimal amount) // متد برای تغییر قاعدهمند
{
if (amount > 0)
_balance += amount;
}
}
توی این مثال، _balance با private مخفی شده و فقط از طریق پراپرتی Balance یا متد Deposit میشه بهش دسترسی داشت. اینجوری مطمئنیم که موجودی حساب طبق قوانین مشخص تغییر میکنه.
کاربرد as در سی شارپ :
روش صحیح استفاده از as در سیشارپ:
در سیشارپ، as برای تبدیل نوع (Casting) استفاده میشود. اگر تبدیل ممکن نباشد، null برمیگرداند. در زیر یک مثال صحیح از استفاده از as در سیشارپ آورده شده است:
کد صحیح در سیشارپ:
using System;
class Program
{
static void Main()
{
object obj = "Hello"; // مقدار obj یک رشته است
string str = obj as string; // تبدیل به string با استفاده از as
if (str != null)
{
Console.WriteLine(str); // خروجی: Hello
}
else
{
Console.WriteLine("Memory type mismatch"); // اگر obj قابل تبدیل به string نباشد
}
// مثال دیگر با یک عدد
object obj2 = 123; // مقدار obj2 یک عدد است
string str2 = obj2 as string; // تبدیل به string با استفاده از as
if (str2 != null)
{
Console.WriteLine(str2);
}
else
{
Console.WriteLine("Memory type mismatch"); // خروجی: Memory type mismatch
}
}
}
توضیحات کد:
تعریف متغیر obj:
object obj = "Hello";
در اینجا، obj از نوع object است و مقدار آن یک رشته ("Hello") است.
استفاده از as:
string str = obj as string;
اگر obj قابل تبدیل به string باشد، str مقدار "Hello" را میگیرد.
در غیر این صورت، str برابر null خواهد بود.
اگر str برابر null نباشد، مقدار آن چاپ میشود.
در غیر این صورت، پیام "Memory type mismatch" نمایش داده میشود.
در مثال دوم، obj2 یک عدد (123) است و نمیتوان آن را به string تبدیل کرد.
بنابراین، str2 برابر null خواهد بود و پیام "Memory type mismatch" چاپ میشود.
تفاوت as با Casting معمولی:
Casting معمولی ((type)variable):
اگر تبدیل ممکن نباشد، یک استثنا (InvalidCastException) پرتاب میشود.
مثال:
object obj = 123;
string str = (string)obj; // خطا: InvalidCastException
استفاده از as:
اگر تبدیل ممکن نباشد، null برمیگرداند و خطایی پرتاب نمیشود.
مثال:
object obj = 123;
string str = obj as string; // str = null
نکات مهم:
as فقط با انواع Reference Type کار میکند:
as فقط برای انواع Reference Type (مانند کلاسها، رشتهها و آرایهها) کار میکند و برای انواع Value Type (مانند int, bool, double و غیره) استفاده نمیشود)
برای انواع Value Type، میتوانید از is و سپس Casting معمولی استفاده کنید.
استفاده از is برای بررسی نوع:
قبل از استفاده از as، میتوانید از is برای بررسی نوع استفاده کنید.
مثال:
if (obj is string)
{
string str = (string)obj;
Console.WriteLine(str);
}
else
{
Console.WriteLine("Memory type mismatch");
}
جمعبندی:
در سیشارپ، از as برای تبدیل نوع (Casting) استفاده میشود و اگر تبدیل ممکن نباشد، null برمیگرداند.
روش دوم ارتباط کلاس و فرم :
استفاده از فرم به عنوان آرگومان
اگر صفحات آموزش های قبلی را خوانده باشید به یاد دارید که از روش استفاده از پراپرتی برای ایجاد ارتباط کلاس با فرم توضیحاتی داده شد در این جلسه بعنوان دومین روش برای ارتباط بین کلاس و فرم روش پاس دادن فرم بعنوان یک آرگومان به متد کلاس توضیحاتی ارائه می شود .این روش به شما امکان میدهد با کد نویسی کمتری به کنترلهای فرم دسترسی داشته باشید .
مثال:
internal class MyClass
{
public void ProcessForm(Form form)
{
CheckBox checkBox = (CheckBox)form.Controls["checkBox1"];
if (checkBox.Checked)
{
// کد مربوط به چکباکس تیک خورده
}
else
{
// کد مربوط به چکباکس تیک نخورده
}
}
}
نحوه استفاده در فضای فرم مثلا در رویداد یک باتن :
private void button1_Click(object sender, EventArgs e)
{
MyClass testClass = new MyClass();
testClass.ProcessForm(this); // this اشاره به فرم فعلی دارد
}
در این مثال، متد ProcessForm در کلاس MyClass به عنوان یک متد تعریف شده است که یک فرم را به عنوان آرگومان دریافت میکند. سپس میتوانید به کنترلهای فرم با استفاده از نام آنها در متد دسترسی پیدا کنید.
مقایسه دو روش
روش اول : استفاده از Property
مزیت: انعطافپذیر است و به شما امکان میدهد تا فقط اطلاعات مورد نیاز را بین کلاس و فرم منتقل کنید.
معایب: اگر بخواهید چندین کنترل را مدیریت میکنید نیاز به تعریف چندین Property دارید .
روش دوم : استفاده از فرم به عنوان آرگومان
مزیت: به شما امکان میدهد به تمام کنترلهای فرم دسترسی داشته باشید و کد کمتری نیاز دارید.
معایب: ممکن است به دلیل دسترسی مستقیم به کنترلهای فرم، کدهای شما کمتر کپسوله شده باشند و در صورت تغییرات در فرم، نیاز به تغییرات بیشتر در کلاس داشته باشید.
در هر صورت هر دو روش دارای مزایا و معایبی هستند و انتخاب بین آنها بستگی به نیاز و طراحی برنامه شما دارد.