IDisposable في C#: كيف لا تقتل أداء التطبيق

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

IDisposable: السلاح السري للأداء الذي تتجاهله الوثائق



هل تساءلت يومًا لماذا يبدأ تطبيق C# الخاص بك في "التباطؤ" بعد عدة ساعات من العمل؟ أو لماذا تتوقف قاعدة البيانات فجأة عن الاستجابة للاستعلامات؟ في 80% من الحالات، يكون السبب هو واجهة واحدة — أو بالأحرى، استخدامها الخاطئ. إنها IDisposable.



على Stack Overflow، جمع سؤال "الاستخدام الصحيح لواجهة IDisposable" آلاف المشاهدات ومئات المناقشات. وهذا ليس من قبيل الصدفة: التعامل الخاطئ مع الموارد هو العدو الأول للأداء في .NET. دعنا نفهم كيف نحول IDisposable من "سحر أسود" إلى أداة قوية للتحسين.



لماذا IDisposable ليست مجرد "علامة اختيار" في الكود؟



كثير من المطورين ينظرون إلى IDisposable على أنها واجب ممل: "يجب تنفيذها — سأنفذها". لكن في الحقيقة، هي حليفك الرئيسي في معركة الأداء. تخيل أن تطبيقك هو مكتب. جامع القمامة (GC) هو عامل النظافة الذي يأتي ليلاً. أما IDisposable فهو مساعدك الشخصي الذي ينظف القمامة فورًا، دون انتظار الصباح.



بدون الاستخدام الصحيح لـ IDisposable، أنت تخاطر بـ:

  • تسرب الذاكرة (memory leaks)
  • حظر الملفات واتصالات قاعدة البيانات
  • انخفاض غير متوقع في الأداء
  • مشاكل في التوسع


الأساسيات: ما هو IDisposable وكيف يعمل؟



IDisposable هي واجهة تحتوي على طريقة واحدة فقط: Dispose(). مهمتها هي تحرير الموارد غير المُدارة (الملفات، اتصالات الشبكة، مقابض النوافذ) فورًا بمجرد عدم الحاجة إليها.



public class ResourceHolder : IDisposable{    private IntPtr _unmanagedResource;    private bool _disposed = false;

public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }

protected virtual void Dispose(bool disposing) { if (_disposed) return;

if (disposing) { // تحرير الموارد المُدارة }

// تحرير الموارد غير المُدارة if (_unmanagedResource != IntPtr.Zero) { Marshal.FreeHGlobal(_unmanagedResource); _unmanagedResource = IntPtr.Zero; }

_disposed = true; }

~ResourceHolder() { Dispose(false); }}


لاحظ GC.SuppressFinalize(this) — هذه نقطة تحسين رئيسية. عندما تستدعي Dispose بشكل صريح، فأنت تقول لـ GC: "لا تستدعي المنهي (finalizer)، لقد فعلت كل شيء بالفعل". هذا يسرع عملية جمع القمامة.



نمط Dispose: التطور من البسيط إلى المعقد



المستوى 1: الأبسط (للموارد المُدارة فقط)



إذا كان صفك يستخدم فقط موارد مُدارة (كائنات IDisposable أخرى)، فإن التنفيذ البسيط كافٍ:



public class SimpleLogger : IDisposable{    private readonly FileStream _logFile;

public SimpleLogger(string path) { _logFile = new FileStream(path, FileMode.Append); }

public void Dispose() { _logFile?.Dispose(); }}


المستوى 2: النمط الكامل (مع المنهي)



للصفوف التي تتعامل مع الموارد غير المُدارة (مثل مقابض WinAPI)، نحتاج إلى النمط الكامل مع المنهي:



public class SafeHandleWrapper : IDisposable{    private SafeHandle _handle;

public SafeHandleWrapper() { _handle = new SafeFileHandle(IntPtr.Zero, true); }

public void Dispose() { Dispose(true); GC.SuppressFinalize(this); }

protected virtual void Dispose(bool disposing) { if (_handle != null && !_handle.IsInvalid) { _handle.Dispose(); } }

// المنهي مطلوب فقط إذا كان هناك

المدونات

توصيات الكتب