واجهة IDisposable وجملة using
جزء من قسم Object Oriented Programming في رحلة C# على Coddy — الدرس 45 من 70.
تحتفظ بعض الكائنات بموارد لا يستطيع جامع المهملات (garbage collector) تنظيفها تلقائيًا - مثل مقابض الملفات، واتصالات قواعد البيانات، ومقابس الشبكة. توفر واجهة IDisposable طريقة قياسية لتحرير هذه الموارد عند الانتهاء من استخدامها.
تقوم الفئة بتنفيذ IDisposable من خلال توفير دالة Dispose():
public class FileHandler : IDisposable
{
private bool disposed = false;
public void Dispose()
{
if (!disposed)
{
Console.WriteLine("Releasing resources...");
disposed = true;
}
}
}يمكنك استدعاء Dispose() يدوياً، ولكن ماذا لو حدث استثناء قبل أن تصل إلى ذلك السطر؟ تضمن عبارة using حدوث عملية التنظيف، حتى عندما تسوء الأمور:
using (FileHandler handler = new FileHandler())
{
// العمل مع المعالج
} // يتم استدعاء Dispose() تلقائياً هناتوفر لغة C# الحديثة بناء جملة أكثر بساطة مع تصريح using، والذي يقوم بالتخلص من الكائن عندما يخرج عن النطاق:
void ProcessFile()
{
using FileHandler handler = new FileHandler();
// التعامل مع handler
} // يتم استدعاء Dispose() عند الخروج من الدالةتعمل جملة using مع أي نوع يقوم بتنفيذ IDisposable. الفئات المضمنة مثل StreamReader و SqlConnection و HttpClient جميعها تقوم بتنفيذ هذه الواجهة، مما يجعل إدارة الموارد بشكل صحيح أمراً سهلاً وموثوقاً.
التحدي
سهللنقم ببناء محاكي لاتصال قاعدة بيانات يوضح الإدارة السليمة للموارد باستخدام واجهة IDisposable وعبارة using. ستقوم بإنشاء فئة تتتبع وقت فتح وإغلاق الاتصالات، مما يضمن تحرير الموارد دائماً بشكل صحيح.
ستقوم بتنظيم الكود الخاص بك عبر ملفين:
DatabaseConnection.cs: أنشئ فئةDatabaseConnectionفي مساحة الأسماءDataAccessالتي تنفذIDisposable. يجب أن يقوم الاتصال الخاص بك بـ:- امتلاك خاصية
ConnectionName(سلسلة نصية، للقراءة فقط) يتم تعيينها من خلال المنشئ (constructor) - تتبع ما إذا كان قد تم التخلص من الاتصال (disposed) باستخدام حقل منطقي (boolean) خاص
- طباعة
Connection opened: {ConnectionName}عند إنشاء الكائن (في المنشئ) - تنفيذ طريقة
Dispose()لطباعةConnection closed: {ConnectionName}- ولكن فقط إذا لم يتم التخلص منه بالفعل (لمنع التخلص المزدوج) - امتلاك طريقة
Query(string sql)تعيدExecuting on {ConnectionName}: {sql}
- امتلاك خاصية
Program.cs: في ملفك الرئيسي، وضح الإدارة السليمة للموارد باستخدام عبارةusingمع اتصال قاعدة البيانات الخاص بك. أنشئ اتصالاً، ونفذ استعلاماً، واترك كتلةusingتتعامل مع عملية التنظيف تلقائياً.
ستتلقى مدخلين:
- اسم الاتصال (على سبيل المثال،
MainDB) - استعلام SQL المراد تنفيذه (على سبيل المثال،
SELECT * FROM Users)
استخدم عبارة using لإنشاء اتصالك. داخل الكتلة، استدعِ طريقة Query واطبع نتيجتها. عندما تنتهي كتلة using، سيتم استدعاء Dispose() تلقائياً.
على سبيل المثال، إذا كانت المدخلات هي MainDB و SELECT * FROM Users، يجب أن يكون المخرج:
Connection opened: MainDB
Executing on MainDB: SELECT * FROM Users
Connection closed: MainDBلاحظ كيف يتم إغلاق الاتصال تلقائياً عند انتهاء كتلة using - لن تضطر أبداً إلى تذكر استدعاء Dispose() يدوياً. يضمن هذا النمط تنظيف الموارد دائماً، حتى في حالة حدوث استثناء داخل الكتلة!
ورقة مرجعية
توفر واجهة IDisposable طريقة قياسية لتحرير الموارد التي لا يستطيع جامع المهملات (garbage collector) تنظيفها تلقائيًا (مقابض الملفات، اتصالات قاعدة البيانات، ومقابس الشبكة).
تقوم الفئة بتنفيذ IDisposable من خلال توفير طريقة Dispose():
public class FileHandler : IDisposable
{
private bool disposed = false;
public void Dispose()
{
if (!disposed)
{
Console.WriteLine("Releasing resources...");
disposed = true;
}
}
}تضمن عبارة using حدوث عملية التنظيف تلقائيًا، حتى عند حدوث استثناءات:
using (FileHandler handler = new FileHandler())
{
// العمل مع المعالج
} // يتم استدعاء Dispose() تلقائيًا هناتوفر لغة C# الحديثة بناء جملة أكثر نظافة مع تصريح using:
void ProcessFile()
{
using FileHandler handler = new FileHandler();
// العمل مع المعالج
} // يتم استدعاء Dispose() عند الخروج من الطريقةتعمل عبارة using مع أي نوع يقوم بتنفيذ IDisposable، بما في ذلك الفئات المضمنة مثل StreamReader و SqlConnection و HttpClient.
جرّب بنفسك
using System;
using DataAccess;
class Program
{
public static void Main(string[] args)
{
string connectionName = Console.ReadLine();
string sqlQuery = Console.ReadLine();
// TODO: استخدم عبارة 'using' لإنشاء DatabaseConnection
// داخل كتلة using:
// 1. استدعِ التابع Query مع sqlQuery
// 2. اطبع نتيجة التابع Query
// سيتم استدعاء التابع Dispose تلقائياً عند انتهاء كتلة using
}
}
يتضمن هذا الدرس اختبارًا قصيرًا. ابدأ الدرس للإجابة عليه وتتبّع تقدمك.
جميع دروس Object Oriented Programming
1أساسيات OOP
الملفات الخارجيةNamespaces والتوجيهاتمقدمة في Classes و Objectsالكلمة المفتاحية 'this'Methods والمعاملاتFields مقابل PropertiesConstructorsObject Initializersمراجعة - آلة حاسبة بسيطة4الوراثة
صيغة الوراثة الأساسية (:)الكلمة المفتاحية 'base'الكلمات المفتاحية Virtual و Overrideالأصناف المختومة (Sealed Classes)الفئة الأساسية 'object'مراجعة - هيكلية الموظفين7ميزات متقدمة
تحميل العوامل (Operator Overloading)المفهرسات (Indexers (this[]))إعادة تعريف ()ToStringدوال التوسعة (Extension Methods)ملخص - Custom List2الخصائص والأعضاء الساكنة (Static Members)
الخصائص المطبقة تلقائيًا (Auto-Implemented Properties)خصائص القراءة فقط والكتابة فقطالحقول والدوال الساكنة (Static)الفئات الساكنة (Static Classes)الأعضاء ذات البنية التعبيرية (Expression-Bodied Members)5تعدد الأشكال والواجهات (Polymorphism & Interfaces)
تعدد الأشكال: وقت التجميع مقابل وقت التشغيلالواجهة مقابل الفئة المجردة (Abstract Class)الواجهات المتعددةالواجهات الصريحةUpcasting و Downcastingملخص - حاسبة الأشكال8مفاهيم البرمجة كائنية التوجه (OOP) المتقدمة
التركيب بدلاً من الوراثةالـ Generics (الأصناف والدوال)الـ Delegates والأحداثالـ Attributes والـ Reflectionواجهة IDisposable وجملة usingأساسيات Dependency Injection