C#: الفئات والكائنات
1. لماذا نحتاج إلى الفئات؟
لغة C# هي لغة موجهة للكائنات، حيث يُعتبر كل شيء تقريبًا كائنًا. كنا في السابق نكتب التعليمات البرمجية باستخدام عبارات من المستوى الأعلى، لكن برامج C# الحقيقية تُبنى من الفئات. الفئة هي مخطط للكائنات — فهي تُحدد البيانات (الحقول) والسلوك (الطرق)، في حين أن الكائن هو مثيل ملموس لفئة ما.
2. تعريف الفئة
استخدم الكلمة الرئيسية class لتعريف فئة. تُكتب أسماء الفئات باستخدام أسلوب PascalCase (بكتابة الحرف الأول من كل كلمة بحرف كبير).
class Person
{
public string Name;
public int Age;
public void SayHello()
{
System.Console.WriteLine($"Hello, I'm {Name}, {Age} years old.");
}
}
publicتعني أن الوصول إلى العضو ممكن من الخارجNameوAgeهما حقول (تخزين البيانات)SayHelloهي طريقة (تحدد السلوك)
3. إنشاء الكائنات
استخدم الكلمة الرئيسية new لإنشاء كائن من فئة:
Person p = new Person();
p.Name = "Tom";
p.Age = 18;
p.SayHello();
Hello, I'm Tom, 18 years old.
يقوم كل new بإنشاء كائن مستقل يحتوي على نسخة خاصة به من الحقول.
4. الحقول وتهيئة الحقول
يمكن تعيين قيم أولية للحقول عند الإعلان عنها:
class Student
{
public string Name = "Unknown";
public int Score = 0;
}
في حالة عدم تعيين أي قيمة أولية، تُعيَّن القيمة الافتراضية للأنواع الرقمية إلى 0، بينما تُعيَّن القيمة الافتراضية للأنواع المرجعية إلى null.
تتحكم مُعدِّلات الوصول في ظهور الحقول:
| المُعدِّل | المعنى |
|---|---|
public |
يمكن الوصول إليه من أي مكان |
private |
لا يمكن الوصول إليها إلا داخل الفئة (الافتراضي) |
عادةً ما يتم تعيين الحقول إلى private، ويتم التحكم في الوصول إليها من خلال الطرق أو الخصائص.
▶ مثال
using System;
class BankAccount
{
private decimal balance = 1000m;
public void Deposit(decimal amount)
{
balance += amount;
Console.WriteLine($"Deposited {amount}, balance is {balance}");
}
public void ShowBalance()
{
Console.WriteLine($"Current balance: {balance}");
}
}
class Program
{
static void Main()
{
BankAccount acc = new BankAccount();
acc.ShowBalance();
acc.Deposit(500m);
}
}
Current balance: 1000
Deposited 500, balance is 1500
5. الطرق
الطرق هي دالات مُعرَّفة داخل فئة ما وتصف سلوك الكائن. يمكن للطرق الوصول إلى الحقول الموجودة داخل الفئة نفسها:
class Calculator
{
private int result = 0;
public void Add(int value)
{
result += value;
}
public int GetResult()
{
return result;
}
}
6. الكلمة الرئيسية «this»
يشير this إلى مثيل الكائن الحالي. ويُستخدم في الغالب لتمييز الحقول عن المعلمات التي تحمل الاسم نفسه:
class Person
{
public string Name;
public int Age;
public Person(string Name, int Age)
{
this.Name = Name;
this.Age = Age;
}
}
- يشير
this.Nameإلى الحقل، ويشيرNameإلى المعلمة - يمكن حذف
thisفي حالة عدم وجود أي لبس
7. منشئات
يتم استدعاء منشئات الكائنات تلقائيًا عند إنشاء كائن ما، وتُستخدم لتهيئة الكائن.
(1) مُنشئ بدون معلمات
في حالة عدم تعريف أي مُنشئ، يقوم المُترجم تلقائيًا بإنشاء مُنشئ بدون معلمات:
class Dog
{
public string Name;
public Dog()
{
Name = "Unnamed";
}
}
(2) مُنشئ مُعَرَّف بالمعلمات
class Dog
{
public string Name;
public Dog(string name)
{
Name = name;
}
}
⚠️ بمجرد تعريف أي مُنشئ، لن يقوم المُترجم بعد ذلك تلقائيًا بإنشاء مُنشئ بدون معلمات.
(3) تحميل الدالة البانية
يمكنك تعريف عدة منشئات بمعلمات مختلفة:
class Dog
{
public string Name;
public int Age;
public Dog()
{
Name = "Unnamed";
Age = 0;
}
public Dog(string name)
{
Name = name;
Age = 0;
}
public Dog(string name, int age)
{
Name = name;
Age = age;
}
}
(4) تسلسل المنشئات
استخدم this(...) لجعل أحد المنشئات يستدعي منشئًا آخر، مما يتيح تجنب تكرار الكود:
class Dog
{
public string Name;
public int Age;
public Dog() : this("Unnamed", 0) { }
public Dog(string name) : this(name, 0) { }
public Dog(string name, int age)
{
Name = name;
Age = age;
}
}
▶ مثال
using System;
class Book
{
public string Title;
public string Author;
public decimal Price;
public Book() : this("Unknown", "Unknown", 0m) { }
public Book(string title, string author) : this(title, author, 29.9m) { }
public Book(string title, string author, decimal price)
{
Title = title;
Author = author;
Price = price;
}
public void PrintInfo()
{
Console.WriteLine($"\"{Title}\" Author: {Author} Price: {Price}");
}
}
class Program
{
static void Main()
{
Book b1 = new Book();
Book b2 = new Book("C# Basics", "Alice");
Book b3 = new Book("Advanced C#", "Bob", 59.9m);
b1.PrintInfo();
b2.PrintInfo();
b3.PrintInfo();
}
}
"Unknown" Author: Unknown Price: 0
"C# Basics" Author: Alice Price: 29.9
"Advanced C#" Author: Bob Price: 59.9
8. مُدمِّرات
يتم استدعاء مُدمِّر الكائن قبل أن يتم جمع القمامة الخاصة به. وصيغة الاستدعاء هي ~ClassName():
class Resource
{
~Resource()
{
Console.WriteLine("Object is being reclaimed");
}
}
📌 تتميز لغة C# بوجود آلية تجميع القمامة التلقائية (GC)، لذا نادرًا ما تحتاج إلى كتابة دالة التدمير يدويًّا. ولا تُستخدم دوال التدمير إلا لتحرير الموارد غير المُدارة (مثل معالجات الملفات واتصالات قواعد البيانات)، وهو ما سيتم مناقشته بالتفصيل في الدروس اللاحقة.
9. مُهيِّئات الكائنات
قدمت لغة C# 3.0 صيغة مُهيِّئ الكائنات، والتي تتيح لك تعيين القيم مباشرةً إلى الحقول أو الخصائص العامة عند استخدام new:
Person p = new Person { Name = "Lily", Age = 20 };
وهذا يعادل:
Person p = new Person();
p.Name = "Lily";
p.Age = 20;
يمكن دمج مُهيِّئات الكائنات مع مُنشِئات الكائنات المعلمة:
Person p = new Person("Lily") { Age = 20 };
▶ مثال
using System;
class Rectangle
{
public double Width = 1;
public double Height = 1;
public double Area()
{
return Width * Height;
}
}
class Program
{
static void Main()
{
Rectangle r1 = new Rectangle();
Rectangle r2 = new Rectangle { Width = 5, Height = 3 };
Rectangle r3 = new Rectangle { Width = 10 };
Console.WriteLine($"r1 area: {r1.Area()}");
Console.WriteLine($"r2 area: {r2.Area()}");
Console.WriteLine($"r3 area: {r3.Area()}");
}
}
r1 area: 1
r2 area: 15
r3 area: 10
10. الفرق بين «new» و«null»
| المفهوم | المعنى |
|---|---|
new ClassName() |
ينشئ كائنًا في الذاكرة المؤقتة، ويعيد مرجعًا إلى الكائن |
null |
لا يشير إلى أي كائن، وهو القيمة الافتراضية لأنواع المرجع |
Person p1 = new Person();
Person p2 = null;
Console.WriteLine(p1 == null);
Console.WriteLine(p2 == null);
False
True
⚠️ يؤدي الوصول إلى عنصر في مرجع null إلى ظهور خطأ NullReferenceException، وهو الخطأ الأكثر شيوعًا في وقت التشغيل في لغة C#:
Person p = null;
p.SayHello();
💡 تأكد من وجود null قبل الدخول:
if (p != null)
{
p.SayHello();
}
❓ أسئلة شائعة
📖 ملخص
- الفئة هي نموذج أساسي للكائنات، يتم تعريفها باستخدام
class - يتم إنشاء الكائنات باستخدام
new، ولكل كائن نسخته الخاصة من الحقول - الحقول تُخزّن البيانات، والأساليب تحدد السلوك، و
public/privateتتحكم في الوصول - يشير
thisإلى المثيل الحالي، ويُستخدم عادةً للتمييز بين الحقول والمعلمات التي تحمل نفس الاسم - يتم استدعاء الدالات البانية تلقائيًا عند إنشاء كائن، ويمكن إعادة تحميلها، كما يمكن استخدام
this(...)للتسلسل - يتم استدعاء مُدمِّرات الكائنات تلقائيًّا بواسطة مُجمِّع القمامة (GC)، ولا توجد حاجة عمومًا لكتابتها يدويًّا
- مُهيِّئات الكائنات
new T { Field = value }تُبسط عملية التعيين newتنشئ كائنًا، وnullتعني عدم وجود مرجع لكائن، ويؤدي الوصول إلى العناصر التابعة ذات القيمة «null» إلى حدوث خطأ
📝 تمارين
- قم بتعريف فئة
Carتحتوي على الحقولBrand(سلسلة) وSpeed(عدد صحيح)، وطريقةAccelerate(int amount)التي تزيد قيمة Speed، ومنشئ بدون معلمات يقوم بتهيئة قيمة Speed إلى 0 - أضف مُنشئًا معلماتيًّا
Car(string brand)إلى الفئةCar، واستخدمthis(...)لتنفيذ تسلسل المُنشئات - استخدم مُهيئ الكائنات لإنشاء كائن
Carوتعيين قيمتي «Brand» و«Speed»، ثم استدعاء الطريقة «Accelerate» وعرض قيمة «Speed» النهائية - اكتب كودًا يوضح ما يلي: قم بإنشاء مرجع
Carمُخصص لـnull، وحاول الوصول إلى أحد عناصره، واستخدم بنية try-catch لالتقاط الاستثناءNullReferenceException، ثم اطبع رسالة