التنفيذ الصحيح لـ IDisposable
الواجهة المستخدمة لتحرير موارد الكائنات في .NET هي واجهة IDisposable، لكن تطبيق هذه الواجهة خاص تمامًا، بالإضافة إلى ذلك، هناك وظيفتان: إنهاء وإغلاق.
توصي MSDN بتنفيذ واجهة IDisposable وفقًا للنمط التالي:
1 public class Foo: IDisposable
2 {
3 التخلص من الفراغ العام ()
4 {
5 التخلص(صحيح);
6 GC.SuppressFinalize(this);
7}
8
9 التخلص من الفراغ الظاهري المحمي (التخلص المنطقي)
10 {
11 إذا (!m_dispose)
12 {
13 إذا (التخلص)
14 {
15 // الافراج عن الموارد المدارة
16}
17
18 // حرر الموارد غير المُدارة
19
20 m_dispose = صحيح؛
واحد وعشرون }
إثنان وعشرون }
ثلاثة وعشرين
24 ~فو()
25 {
26 التخلص(خطأ);
27}
28
29 بول خاص م_تخلص؛
30}
31
32
توجد في الواقع وظيفتان لتحرير الموارد في كائنات .NET: التخلص والإنهاء. الغرض من Finalize هو تحرير الموارد غير المُدارة، بينما يتم استخدام Dispose لتحرير جميع الموارد، بما في ذلك المُدارة وغير المُدارة.
في هذا الوضع، تستخدم وظيفة التخلص من الفراغ (التخلص المنطقي) معلمة التخلص لتمييز ما إذا كان يتم استدعاؤها حاليًا بواسطة Dispose(). إذا تم استدعاؤها بواسطة Dispose()، فيجب إصدار كل من الموارد المُدارة وغير المُدارة في نفس الوقت. إذا تم استدعاؤه بواسطة ~Foo() (أي Finalize() الخاص بـ C#)، فأنت بحاجة فقط إلى تحرير الموارد غير المُدارة.
وذلك لأن وظيفة Dispose() يتم استدعاؤها بشكل صريح بواسطة تعليمات برمجية أخرى وتتطلب تحرير الموارد، بينما يتم استدعاء Finalize بواسطة GC. قد لا تحتاج الكائنات المُدارة الأخرى المشار إليها بواسطة Foo إلى التدمير عند استدعاء GC، وحتى إذا تم تدميرها، فسيتم استدعاؤها بواسطة GC. لذلك، يجب تحرير الموارد غير المُدارة فقط في النسخة النهائية. من ناحية أخرى، نظرًا لأنه تم إصدار الموارد المُدارة وغير المُدارة في Dispose()، فليس من الضروري استدعاء Finalize مرة أخرى عند إعادة تدوير الكائن بواسطة GC، لذا اتصل بـ GC.SuppressFinalize(this) في Dispose() لتجنب تكرار الاتصال وضع اللمسات الأخيرة.
ومع ذلك، لا توجد مشكلة حتى إذا تم استدعاء الإنهاء والتخلص بشكل متكرر، نظرًا لوجود المتغير m_dispose، سيتم تحرير المورد مرة واحدة فقط، وسيتم تجاهل الاستدعاءات المتكررة.
ولذلك فإن النمط أعلاه يضمن:
1. وضع اللمسات النهائية على إصدارات الموارد غير المُدارة فقط؛
2. التخلص من الإصدارات المُدارة وغير المُدارة؛
3. لا توجد مشكلة في استدعاء الإنهاء والتخلص بشكل متكرر؛
4. يتشارك الإنهاء والتخلص في نفس استراتيجية إصدار الموارد، لذلك لا يوجد تعارض بينهما .
في C#، يجب تنفيذ هذا النمط بشكل صريح، حيث تمثل وظيفة ~Foo() الخاصة بـ C# Finalize(). في C++/CLI، يتم تنفيذ هذا الوضع تلقائيًا، لكن مدمر فئة C++ مختلف.
وفقًا لدلالات C++، يتم استدعاء المدمر عندما يخرج عن النطاق أو يحذف. في Managed C++ (أي C++ المُدارة في .NET 1.1)، يكون المدمر مكافئًا لأسلوب Finalize() في CLR، والذي يتم استدعاؤه بواسطة GC أثناء تجميع البيانات المهملة، لذلك، توقيت الاستدعاء غير واضح. في C++/CLI في .NET 2.0، تم تعديل دلالات المدمرات لتكون مكافئة لطريقة Dispose()، والتي تتضمن شيئين:
1. تطبق جميع فئات CLR في C++/CLI الواجهة IDisposable، لذا يمكنك استخدام الكلمة الأساسية use للوصول إلى مثيلات هذه الفئة في C#.
2. لم يعد المدمر مكافئًا لـ Finalize().
بالنسبة للنقطة الأولى، هذا شيء جيد، وأعتقد أن Dispose() هو أقرب إلى مدمر C++ من الناحية الدلالية. فيما يتعلق بالنقطة الثانية، قامت Microsoft بإنشاء امتداد من خلال تقديم الدالة "!"، كما هو موضح أدناه:
1 فئة المرجع العام Foo
2 {
3 عامة:
4Foo();
5 ~Foo();
6 !Foo();
7 };
8
تحل الوظيفة "!" (لا أعرف حقًا ما أسميها) محل وظيفة Finalize() الأصلية في Managed C++ ويتم استدعاؤها بواسطة GC. توصي MSDN أنه من أجل تقليل تكرار التعليمات البرمجية، يمكنك كتابة تعليمات برمجية مثل هذا:
1 ~Foo()
2 {
3 // الافراج عن الموارد المدارة
4 هذا->!Foo();
5}
6
7 !فو()
8 {
9 //حرر الموارد غير المُدارة
10}
11
بالنسبة للفئة المذكورة أعلاه، فإن رمز C# المطابق الذي تم إنشاؤه بواسطة C++/CLI هو في الواقع كما يلي:
1 فئة عامة Foo
2 {
3 فراغ خاص !Foo()
4 {
5 // حرر الموارد غير المُدارة
6}
7
8 فراغ خاص ~Foo()
9 {
10 // حرر الموارد المُدارة
11 !فو();
12}
13
14 publicFoo()
15 {
16}
17
18 التخلص من الفراغ العام ()
19 {
20 التخلص(صحيح);
21 GC.SuppressFinalize(this);
إثنان وعشرون }
ثلاثة وعشرين
24 التخلص من الفراغ الظاهري المحمي (التخلص المنطقي)
25 {
26 إذا (التخلص)
27 {
28 ~فو();
29 }
30 آخر
31 {
32 محاولة
33 {
34!فو();
35}
36 أخيرا
37 {
38 قاعدة.الانتهاء();
39 }
40}
41 }
42
43 إنهاء الفراغ المحمي ()
44 {
45 التخلص(خطأ);
46 }
47 }
48
نظرًا لأن ~Foo() و !Foo() لن يتم استدعاؤهما بشكل متكرر (على الأقل يعتقد MS ذلك)، لا توجد متغيرات في هذا الكود مماثلة لمتغيرات m_dispose السابقة، ولكن البنية الأساسية هي نفسها.
علاوة على ذلك، يمكنك أن ترى أنه ليس في الواقع ~Foo() و !Foo() هما التخلص والإنهاء، ولكن مترجم C++/CLI ينشئ وظيفتين للتخلص والإنهاء ويستدعيهما في الوقت المناسب. لقد قام C++/CLI بالكثير من العمل بالفعل، لكن المشكلة الوحيدة هي أنه يعتمد على اتصال المستخدم !Foo() في ~Foo().
فيما يتعلق بإصدار الموارد، آخر شيء يجب ذكره هو وظيفة الإغلاق. إنها تشبه إلى حد كبير وظيفة Dispose. وفقًا لـ MSDN، يتم توفير هذه الوظيفة لجعل المستخدمين يشعرون براحة أكبر، لأن المستخدمين أكثر اعتيادًا على استدعاء Close() لكائنات معينة، مثل الملفات.
ومع ذلك، بعد كل شيء، تقوم هاتان الوظيفتان بنفس الشيء، لذا فإن الكود الموصى به بواسطة MSDN هو:
1 إغلاق الفراغ العام ()
2 {
3 التخلص(();
4}
5
6
هنا، يتم استدعاء وظيفة التخلص بدون معلمات مباشرة للحصول على نفس دلالات التخلص. يبدو أن هذا مثاليًا، ولكن من ناحية أخرى، إذا تم توفير التخلص والإغلاق في نفس الوقت، فسيؤدي ذلك إلى حدوث بعض الارتباك للمستخدمين. وبدون رؤية تفاصيل الكود، من الصعب معرفة الفرق بين هاتين الوظيفتين. لذلك، تنص مواصفات تصميم التعليمات البرمجية لـ .NET على أنه يمكن للمستخدمين في الواقع استخدام إحدى هاتين الوظيفتين فقط. لذا فإن النمط المقترح هو:
1 public class Foo: IDisposable
2 {
3 إغلاق الفراغ العام ()
4 {
5 التخلص();
6}
7
8 IDisposable.Dispose () باطلة
9 {
10 التخلص(صحيح);
11 GC.SuppressFinalize(this);
12}
13
14 التخلص من الفراغ الظاهري المحمي (التخلص المنطقي)
15 {
16 // نفس ما كان عليه من قبل
17}
18}
19
يتم هنا استخدام ما يسمى بالتنفيذ الصريح للواجهة: void IDisposable.Dispose(). لا يمكن الوصول إلى هذا التنفيذ الصريح إلا من خلال الواجهة، ولكن ليس من خلال فئة التنفيذ. لذلك:
1 فو فو = جديد فو ()؛
2
3 foo.Dispose(); // خطأ
4 (foo as IDisposable).Dispose(); // صحيح
5
هذا يعتني بكليهما. بالنسبة لأولئك الذين يحبون استخدام Close، يمكنهم استخدام foo.Close() مباشرة، ولن يتمكنوا من رؤية Dispose(). بالنسبة لأولئك الذين يحبون التخلص، يمكنه تحويل النوع إلى IDisposable للاتصال به، أو استخدام عبارة الاستخدام. فرحة كبيرة لكليهما!
http://www.cnblogs.com/xlshcn/archive/2007/01/16/idisposable.html