جدول قرار: إنهاء أم استمرار بعد استثناء غير متوقّع

· آخر تحديث: · · تطوير Windows, معالجة الاستثناءات, تصميم, C# / .NET, موثوقية

سجل التعديلات (3 تحديثات، آخر تحديث 3 Sep، 2026)

سجل بالتغييرات التي أُجريت على هذا المقال. وحيثما حُفظت نسخة سابقة، تبقى متاحة للقراءة عبر رابط دائم يحمل معرّف DOI.

أُضيفت روابط الاستشارة الموجودة في الأصل الياباني (consultation_services). ولم يتغيّر نصّ المقالة نفسه. قراءة النسخة السابقة لهذا التحديث (DOI: 10.5281/zenodo.22240846)
أُعيدَت الترجمة العربية كترجمة كاملة عن النص الياباني الأصلي، وأُضيفَت خريطة المعرفة.
أعيدت الترجمة كترجمة كاملة عن النص الياباني الأصلي. كانت النسخة العربية السابقة مختصراً يسقط أبواباً وجداول ورسوم Mermaid وتعليقات الأشكال وFAQ. أُعيدت هذه العناصر وفق الأصل الياباني، والادّعاءات التقنية مطابقة للنسخة اليابانية.
النشر الأول
الاستشهاد بهذا المقال(DOI: 10.5281/zenodo.21621390)

هذا المقال محفوظ على Zenodo. يرد أدناه معرّف DOI الذي يشير دائمًا إلى أحدث نسخة، ومعرّف DOI المثبَّت على النسخة التي تقرؤها.

小村 豪 (2026). جدول قرار: إنهاء أم استمرار بعد استثناء غير متوقّع. شركة كومورا سوفت ذ.م.م.. https://doi.org/10.5281/zenodo.21621390 https://comcomponent.com/ar/blog/2026/03/16/005-unexpected-exception-exit-or-continue-decision-table/

DOI (أحدث نسخة)
10.5281/zenodo.21621390
DOI (هذه النسخة)
10.5281/zenodo.22279801

تنزيل قائمة تحقّق Excel بصفحتي اليابانية والإنجليزية

الملف صفحتان Checklist-ja و Checklist-en، ويفكّك محاور الحكم في هذه المقالة إلى 27 بنداً في 4 فئات: شروط الاستمرار / شروط الإنهاء / سياسة التنفيذ / ترتيب الحكم. عمودا Status و Notes فارغان، فيصلحان كما هما سجلاً لمعالجة عطل أو قائمة مراجعة لتصميم.

عند الحديث عن استثناء غير متوقّع ننجرّ سريعاً إلى ثنائية «نُسقط أم نعمل catch ونتابع». في الميدان هذا الوضع للثنائية أخشن ممّا ينبغي.

ما يُراد رؤيته فعلاً هو هل يمكن حصر النطاق الذي قد يكون قد تكسّر.

  • هل يمكن إنهاء تلك العملية وحدها بالفشل
  • هل يكفي إعادة تهيئة تلك الشاشة / ذلك الاتّصال / ذلك العامل
  • أم أن اتّساق العملية كلّها صار مشبوهاً

بهذا الترتيب يصير الترتيب أسهل كثيراً.

ترتيب فحص النطاق المكسورمخطّط يبيّن عند وقوع استثناء غير متوقّع ترتيب فحص النطاق الذي قد يكون قد تكسّر: هل يمكن إنهاء تلك العملية وحدها بالفشل، هل يكفي إعادة تهيئة النظام الفرعي وحده، أم أن اتّساق العملية كلّها مشبوه.هل يمكن فشل تلك العملية وحدهاهل إعادة تهيئة الشاشة أو الاتّصال كافيةهل العملية كلّها مشبوهة

الشكل 1: افحص النطاق المحتمل للكسر بالترتيب: العملية، ثم النظام الفرعي، ثم العملية كلّها.

تفترض المقالة تطبيقات Windows بـ C# / .NET، وتطبيقات مقيمة، وخدمات Windows، وأدوات ربط أجهزة، وتجمع في جدول قرار شروط جواز الاستمرار بعد استثناء غير متوقّع، وشروط أفضليّة الإنهاء.

1. الخلاصة أوّلاً

  • ابتلاع الاستثناء بـ catch (Exception) ومتابعة العمل خطر في الغالب.
  • الاستمرار جائز عندما تجتمع ثلاثة: يمكن رمي وحدة الفشل، يمكن إعادة الحالة المشتركة، يمكن شرح الآثار الجانبيّة الخارجيّة.
  • عملية واجهة واحدة، أو إدخال واحد، أو وظيفة واحدة، إن كان حدّ المعالجة واضحاً، قد تسمح بالاستمرار.
  • بالمقابل، الحالة المشتركة القابلة للكتابة، والحلقة المقيمة، والخيط الرئيسي، ومعالجة الإقلاع، والحدود الأصليّة، ورائحة تلف الذاكرة، تميل إلى الإنهاء.
  • استثناءات تشكّك في «سلامة العملية كلّها» مثل StackOverflowException و AccessViolationException و OutOfMemoryException لا يُبنى عليها افتراض الاستمرار.
  • في WPF وWindows Forms طريق لالتقاط استثناء غير معالج والمتابعة ظاهرياً، لكن إمكان المتابعة وسلامة المتابعة أمران مختلفان.
  • في خدمة طويلة التشغيل أو تطبيق مراقبة، السقوط وإعادة التشغيل غالباً أأمن وأسهل تشخيصاً من البقاء نصف مكسور.

محور الحكم إذن: هل يمكن استرداد الشرط الثابت.

الشروط الثلاثة لجواز الاستمرارمخطّط يبيّن أن الاستمرار جائز فقط عندما تجتمع ثلاثة: يمكن رمي وحدة الفشل، يمكن إعادة الحالة المشتركة، يمكن شرح الآثار الجانبيّة الخارجيّة، وأن محور الحكم هو إمكان استرداد الشرط الثابت.يمكن رمي وحدة الفشلإن اجتمعت الثلاثة جاز الاستمراريمكن إعادة الحالة المشتركةيمكن شرح الآثار الجانبيّة الخارجيّةالمحور: هل يمكن استرداد الشرط الثابت

الشكل 2: الاستمرار جائز فقط عندما تجتمع شروط وحدة الفشل والحالة المشتركة والآثار الجانبيّة الخارجيّة.

1.1 ألفاظ هذه المقالة

قبل جدول القرار نثبّت خمس كلمات تتكرّر.

المصطلح معناه في هذه المقالة
الشرط الثابت (invariant) وعد حالة يجب أن يبقى قائماً قبل المعالجة وبعدها. مثل «مجموع التفاصيل يطابق قيمة التجميع»، «المحتوى في الذاكرة المؤقّتة وفي قاعدة البيانات متوافق»، «كل اتّصال مفتوح مسجّل في قائمة الإدارة». إن انكسر وبقي التشغيل، صارت كل معالجة لاحقة مشبوهة
وحدة الفشل النطاق الذي يمكن رميه كاملاً عند الفشل. عملية واحدة، شاشة واحدة، وظيفة واحدة، اتّصال واحد
أثر جانبي خارجي تغيير خرج من العملية. تحديث قاعدة بيانات، كتابة ملف، إرسال بريد، أمر إلى جهاز: ما لا يُرجع بـ catch
نظام فرعي وحدة يمكن إيقافها وإعادة تهيئتها معاً. اتّصال، شاشة، عامل، عملية ابن
FailFast Environment.FailFast. واجهة برمجيّة تُنهي العملية فوراً دون تشغيل try / finally ولا المنهي، والتفاصيل في 9.7

في المخطّط، يشير الخطّ المتّصل إلى علاقة قائمة دائماً، ويشير الخطّ المتقطّع إلى علاقة مشروطة (شروط قيامها مذكورة في شرح كلّ علاقة في الصفحة التفصيليّة). القائمة الكاملة للعلاقات (المجموع 21، مع الأدلّة ودرجة اليقين) وتعريفات المفاهيم الرئيسة مجمّعة في صفحة تفاصيل خريطة المعرفة (باليابانية). البيانات: JSON-LD / Turtle

2. ما تعنيه هذه المقالة بـ «استثناء غير متوقّع»

2.1 افصل المتوقّع عن غير المتوقّع

الاستثناء النادر والاستثناء غير المتوقّع ليسا الشيء نفسه.

هذه مثلاً قد تُعدّ متوقّعة وإن قلّت تكرارها.

  • اختار المستخدم ملفاً غير موجود
  • انتهت مهلة الجهة المقابلة مؤقّتاً
  • سطر واحد في CSV الاستيراد كان تالفاً
  • خرج OperationCanceledException من عملية إلغاء
  • تريد فشل تلك المعالجة وحدها لمخالفة قاعدة عمل

هذه أنواع يمكن تصميم التعامل مع فشلها مسبقاً.

أما الاستثناء غير المتوقّع الذي تعالجه هذه المقالة أساساً فهو من هذا القبيل.

  • انكسر افتراض في كودك فخرج NullReferenceException أو InvalidOperationException
  • قفز استثناء في منتصف تحديث حالة مشتركة، وصار مدى الانعكاس مشبوهاً
  • سقطت حلقة مراقبة أو حلقة أم لمعالجة الرسائل
  • ظهر خلل عند حدود COM / P/Invoke / SDK مورّد
  • استثناء يجعل فحص سلامة العملية أحمر أصلاً، مثل AccessViolationException أو StackOverflowException

أي: «بعد وقوع هذا الاستثناء، لا ندري إن كان ما زال يمكن الوثوق بحالة التطبيق».

الحدّ بين المتوقّع وغير المتوقّعمخطّط يبيّن أن الاستثناء الذي يمكن تصميم التعامل معه مسبقاً يُعدّ متوقّعاً حتى إن قلّ تكراره، وأن غير المتوقّع في هذه المقالة هو ما لا ندري بعده إن كان يمكن الوثوق بحالة التطبيق.يمكنلا يمكنوقع استثناءهل يمكن تصميم التعامل مسبقاًمتوقّع (ولو ندر)غير متوقّع (الوثوق بالحالة غير معلوم)

الشكل 3: الاستثناء النادر وغير المتوقّع شيئان مختلفان، والحدّ هو إمكان تصميم التعامل مسبقاً.

2.2 تبدو ثنائية وهي في الحقيقة ثلاثيّة

ما يعقّد الحديث هو اعتبار الاستمرار نوعاً واحداً.

في الميدان ينقسم تقريباً إلى ثلاث درجات.

الاختيار المعنى
أفشل تلك العملية وحدها وأستمر تبقى الشاشة، وتُعدّ عملية الحفظ أو الاستيراد هذه فاشلة
أوقف النظام الفرعي وحده وأستمر أعيد تهيئة الاتّصال أو الشاشة أو العامل أو العملية الابن فقط
أُنهي العملية نطاق تلف الحالة غير مقروء، فنفترض إعادة التشغيل

حتى «استمرار التطبيق» وزنه يختلف بين المتابعة كأن شيئاً لم يكن وفصل الجزء المكسور ثم الاستمرار.

الاستمرار نفسه يختلف وزنهمخطّط يبيّن أن استمرار التطبيق وزنه يختلف بين المتابعة كأن شيئاً لم يكن وفصل الجزء المكسور ثم الاستمرار.استمرار التطبيقالمتابعة كأن شيئاً لم يكنفصل الجزء المكسور ثم الاستمرارالاستمرار نفسه يختلف وزنه

الشكل 4: لا تعامل الاستمرار نوعاً واحداً، وميّزه عن استمرار يفصل الجزء المكسور.

3. جدول القرار الذي يُنظر إليه أولاً

3.1 الصورة الكلّية

ابدأ بهذا الجدول فيتحدّد الاتجاه غالباً.

الوضع الاختيار الأوّل السبب
فشل إدخال واحد أو عملية شاشة واحدة أو وظيفة واحدة، ويمكن رمي الحالة أميل إلى الاستمرار يمكن حصر وحدة الفشل
بعد الاستثناء يمكن إتلاف الكائن أو الاتّصال المستهدف وإعادة بنائه أميل إلى إعادة تهيئة النظام الفرعي يمكن توطين النطاق المكسور
حُدّثت حالة مشتركة جزئياً ولا يُعرف مدى الانعكاس أميل إلى الإنهاء قد يكون الشرط الثابت قد انكسر
أثر جانبي خارجي ناقص في قاعدة بيانات / ملف / أمر جهاز، ولا يمكن شرح التكرار أو عدم الانعكاس أميل إلى الإنهاء الاتّساق مع العالم الخارجي غير مقروء
سقطت حلقة مراقبة أو إعادة اتّصال أو معالجة رسائل أم على استثناء غير متوقّع أميل إلى الإنهاء الموت الصامت لجزء من الوظائف يسهّل التحوّل إلى زومبي
فشل في معالجة الإقلاع أو قراءة الإعداد أو تكوين DI أو تهيئة اعتماد لازم أميل إلى إنهاء فشل الإقلاع الإقلاع الناقص أخطر
AccessViolationException أو StackOverflowException أو OutOfMemoryException خطير أو رائحة تلف أصلي أميل إلى إنهاء فوري سلامة العملية كلّها مشبوهة
المعالجة الخطرة معزولة في عملية ابن، والأب سليم الأب يستمر، والابن يُعاد تشغيله نطاق العطل مفصول
نعملالانعملانعملانعماستثناء غير متوقّعرائحة تلف ذاكرة / نفاذ مكدّس / نفاذ موارد قاتل؟إنهاء / FailFast / إعادة تشغيلهل يمكن رمي وحدة الفشل؟أميل إلى الإنهاءهل يمكن التراجع عن الحالة المشتركة / إعادة تهيئتها؟إيقاف نظام فرعي أو إنهاءهل يمكن شرح الآثار الجانبيّة الخارجيّة؟استمر مع فشل تلك العملية وحدها

الشكل 5: مسار يقرّر الاتجاه بفحص رائحة التلف، ثم وحدة الفشل، ثم الحالة المشتركة، ثم الآثار الجانبيّة الخارجيّة.

«رائحة تلف الذاكرة» في أوّل تفرع للشكل تُعالج في 3.4، و FailFast أعلى اليمين في 9.7.

3.2 ما يُنظر إليه قبل نوع الاستثناء

لا تحسم بنوع الاستثناء وحده. ما يُراد فحصه أولاً من هذا القبيل.

موضع النظر ماذا تفحص
أين وقع حدث واجهة، وظيفة واحدة، حلقة أم، معالجة إقلاع، حدود أصليّة
إلى أين وصل هل تغيّرت حالة الذاكرة أو قاعدة البيانات أو الملف أو الجهاز في المنتصف
النطاق القابل للكسر ذلك الكائن فقط، أم الشاشة كلّها، أم العملية كلّها
إمكان التراجع هل يمكن الإتلاف وإعادة البناء، أو الرجوع بمعاملة
أثر جانبي خارجي أُرسل أم لم يُرسل، هل إعادة التنفيذ آمنة، هل يمكن تعويض
مراقبة وإعادة تشغيل هل هناك إعادة تشغيل تلقائي أو مسار استرداد بعد الإسقاط

3.3 استثناءات عالية الخطورة

لا حاجة لتفصيل كل نوع، لكن هناك ما لا يُنظر إليه بافتراض الاستمرار.

الاستثناء / العلامة الاختيار الأوّل سبب النظر
StackOverflowException أميل إلى إنهاء فوري مكدّس الاستدعاء منهار، فيصعب افتراض استرداد عادي
AccessViolationException أميل إلى إنهاء فوري وصول غير قانوني إلى ذاكرة محميّة، فيُشتبه بحدود أصليّة أو تلف ذاكرة
OutOfMemoryException أميل إلى الإنهاء معالجة الاسترداد التي تفترض تخصيصاً إضافياً تصير غير مستقرّة
NullReferenceException / InvalidOperationException غير متوقّع حسب السياق لكن أميل إلى الإنهاء انكسار افتراضك، وقد يبقى تغيير جزئي
استثناء غير متوقّع تسرّب من الحلقة الأم أميل إلى الإنهاء خطر بقاء العملية بعد موت نواة الوظيفة
خلل من callback في COM / P/Invoke / SDK مورّد إنهاء فوري إلى إنهاء قوي يصعب الحكم على السلامة من الجانب المُدار وحده

3.4 على أي شيء تستند «رائحة تلف الذاكرة» و«التحوّل إلى زومبي»

اللفظان يبدوان حسّيين، لكن ما يُنظر إليه محدّد.

أولاً أعراض الاشتباه بتلف الذاكرة.

ما يظهر أين يُنظر
تسقط العملية برمز استثناء 0xc0000005 (انتهاك وصول) أو 0xc0000374 (تلف كومة) عارض الأحداث > سجلات Windows > Application، «خطأ تطبيق» (معرّف الحدث 1000)
موضع السقوط يختلف كل مرّة. يسقط في كود لم تلمسه للتو السجل، مكدّس الاستدعاء في dump
يسقط فقط عند لمس كائن أو مقبض كان يُفترض تحريره خطوات إعادة الإنتاج، dump
يسقط في تحرير أصلي (free / delete / تحرير COM) مكدّس الاستدعاء في dump (مثل السقوط داخل ntdll.dll)
قيمة لم تُلمس تُستبدل. النتيجة تتغيّر على الدخل نفسه سجل الدخل والخرج، مقارنة إعادة التشغيل

0xc0000005 هو STATUS_ACCESS_VIOLATION، و 0xc0000374 هو STATUS_HEAP_CORRUPTION، وكلاهما معرّف في قائمة NTSTATUS لدى Microsoft. تلف الكومة خصوصاً لا يسقط لحظة الإتلاف، بل عند الجانب الذي يلمس الكومة التالفة تالياً، لذلك موضع السقوط ليس بالضرورة الجاني. إن ظهرت أعراض بهذا الشكل، فالأأمن أن تعتبر catch في الجانب المُدار والمتابعة بلا معنى.

انحراف موضع السقوط عند تلف الكومةمخطّط يبيّن أن تلف الكومة لا يسقط لحظة الإتلاف بل عند الجانب الذي يلمس الكومة التالفة تالياً، لذلك موضع السقوط ليس بالضرورة الجاني.تُتلف الكومة في موضع مالا يسقط في تلك اللحظةيسقط عند من يلمس تالياًموضع السقوط ليس بالضرورة الجاني

الشكل 6: تلف الكومة يسقط عند من يلمس تالياً، فينحرف موضع السقوط عن موضع الإتلاف.

ثانياً أعراض الاشتباه بالتحوّل إلى زومبي.

ما يظهر أين يُنظر
العملية حيّة لكن زمن آخر معالجة لا يتحدّث سجل زمن آخر معالجة، heartbeat
عدد المتراكم في الطابور أو مجلد الاستقبال يزيد وحده طول الطابور، عدد الملفات غير المعالجة
السجل توقّف فجأة من لحظة معيّنة سجل التطبيق
الشاشة قابلة للتشغيل لكن التحديث في الخلف متوقّف مطابقة العرض مع البيانات الفعلية
عدد خيوط العامل أقل من المتوقّع سجل تشخيص، قائمة خيوط Process Explorer

الزومبي ليس عدم السقوط، بل البقاء حيّاً دون عمل. إن جهّزت مسبقاً مؤشّرات «أنه يعمل» مثل زمن آخر معالجة وعدد المتراكم وheartbeat، صار حكم الاستمرار أو الإنهاء وتتبع التحقيق أسهل كثيراً.

مؤشّرات التقاط الزومبيمخطّط يبيّن أن الزومبي بقاء حيّ دون عمل، وأن تجهيز مؤشّرات تدل على أنه يعمل مثل زمن آخر معالجة وعدد المتراكم وheartbeat يسهّل الحكم وتتبع التحقيق.زمن آخر معالجةجهّز مؤشّرات العمل مسبقاًعدد المتراكمheartbeatحكم الاستمرار أو الإنهاء يصير أسهلتتبع التحقيق يصير أسهل

الشكل 7: التقط الزومبي بمؤشّر يدل على أنه يعمل، لا على أنه لم يسقط.

4. احكم بحسب موضع الوقوع

4.1 أحداث الواجهة

أحداث واجهة مثل نقر زر، وانتقال شاشة، وبحث، واختيار ملف، فيها هامش استمرار أكبر نسبياً. بشرط.

يسهل الاستمرار في حالات كهذه.

  • فشل قبل القراءة، وحالة العمل لم تُلمس بعد
  • تلفت حالة مؤقّتة داخل حوار فقط، وتُرمى بإغلاق الشاشة
  • يمكن إعادة بناء ViewModel أو الاتّصال بعد الاستثناء
  • يمكن إخبار المستخدم بصدق أن «هذه العملية فشلت»

بالمقابل، يميل الأمر إلى الإنهاء إن صار كالتالي.

  • حُدّثت حالة الشاشة وحالة النطاق جزئياً معاً
  • لُمست حالة مشتركة تراها شاشات أخرى، مثل static / singleton / ذاكرة مؤقّتة
  • بعد الاستثناء بقي نشاط الزر أو حالة الاختيار وحدهما والاتّساق غير مفهوم
  • وقع استثناء غير متوقّع على خيط الواجهة، وصار مدى تقدّم الرسم أو الإشعار مشبوهاً
حدّ استثناء عملية الواجهةمخطّط يبيّن أن استثناء حدث الواجهة يسهل استمراره إن لُمست حالة مؤقّتة تُرمى بالإغلاق فقط، ويميل إلى الإنهاء إن لُمست حالة مشتركة تراها شاشات أخرى أو تحديث جزئي.حالة مؤقّتة تُرمى فقطحالة مشتركة أو تحديث جزئياستثناء غير متوقّع في عملية واجهةأي حالة لُمستاستمر مع فشل تلك العملية وحدهاأميل إلى الإنهاء

الشكل 8: أحداث الواجهة هامش استمرارها كبير، لكن لمس الحالة المشتركة يغيّر الكلام.

4.2 وظائف / طلبات تُعالج واحدة واحدة

هنا حدّ يسهل فيه الاستمرار.

  • رسالة واحدة
  • ملف واحد
  • طلب HTTP واحد
  • وظيفة استيراد واحدة
  • عنصر دفعة واحدة

إن كانت الوحدة واضحة، افشل تلك الواحدة وانتقل إلى التالية.

بشروط.

  • وحدة الفشل واضحة من الخارج
  • التغيير الجزئي يُسوّى بمعاملة أو تعويض
  • إعادة تمرير المعالجة نفسها لا تُفسد النتيجة
  • يمكن تهريب الفشل إلى طابور عزل أو سجل خطأ

4.3 حلقة مقيمة / مراقبة / معالجة طابور

هنا أسوأ موضع لمتابعة فجّة.

مثلاً:

  • حلقة إعادة اتّصال
  • حلقة مراقبة
  • حلقة استهلاك طابور
  • استعلام دوري
  • مراقبة حالة جهاز
  • معالجة مقيمة لتطبيق صينية

المخيف هنا أن تموت الحلقة الأم باستثناء غير متوقّع واحد، وتبقى العملية وحدها.

هنا يحسن فصل السياسة.

  • التقط الاستثناء المتوقّع عند حدّ معالجة كل عنصر
  • إن تسرّب استثناء غير متوقّع من الحلقة الأم، مل إلى إنهاء العملية
فصل السياسة في الحلقة المقيمةمخطّط يبيّن فصل السياسة في المعالجة المقيمة: التقاط الاستثناء المتوقّع عند حدّ معالجة كل عنصر، والميل إلى إنهاء العملية إن تسرّب استثناء غير متوقّع من الحلقة الأم.حلقة مقيمةحدّ معالجة كل عنصرالحلقة الأمالتقط الاستثناء المتوقّعإن تسرّب غير المتوقّع فمل إلى الإنهاءامنع بقاء العملية وحدها

الشكل 9: افصل سياسة حدّ العنصر عن الحلقة الأم، واربط استثناء الحلقة الأم غير المتوقّع بالإنهاء.

4.4 معالجة الإقلاع

إن جعلت فشل الإقلاع «نقلع ثم نفكّر»، بقيت وظائف ناقصة تعمل، وصار عزل السبب لاحقاً أصعب.

  • لا تُقرأ إعدادات لازمة
  • فشل ترحيل إصدار / هجرة
  • مجلد أو شهادة لازمة غير موجودة
  • فشلت تهيئة خدمة محوريّة
  • تكوين الاعتمادات مكسور

هنا الإنهاء بفشل إقلاع أوضح.

4.5 الحدود الأصليّة / COM / P/Invoke / unsafe

هنا إطار أشد قليلاً.

  • COM
  • P/Invoke
  • ما وراء C++/CLI
  • SDK مورّد
  • كود أصلي يعود بـ callback
  • معالجة فيها unsafe

إن ظهر ما يلي فمل إلى الإنهاء.

  • AccessViolationException
  • أعراض اشتباه تلف كومة أو double free
  • رائحة خلل مقبض أو وصول بعد التحرير
  • موت مفاجئ عند حدّ callback
التعامل مع خلل الحدود الأصليّةمخطّط يبيّن أنه عند حدود أصليّة مثل COM أو P/Invoke، إن ظهر AccessViolationException أو أعراض اشتباه تلف كومة أو موت مفاجئ عند حدّ callback فمل إلى الإنهاء، وأن هذا الحدّ يُنظر إليه بإطار أشد.AccessViolationExceptionأميل إلى الإنهاءأعراض تلف كومة أو double freeموت مفاجئ عند حدّ callbackانظر الحدود الأصليّة بإطار أشد

الشكل 10: إن ظهرت هذه الأعراض عند الحدود الأصليّة، اترك افتراض الاستمرار ومل إلى الإنهاء.

5. شروط جواز الاستمرار

إن جُمعت شروط جواز الاستمرار صارت كالتالي. يفترض أن تجتمع تقريباً.

الشرط المعنى
وحدة الفشل واضحة عملية واحدة، شاشة واحدة، وظيفة واحدة، اتّصال واحد: وحدة الرمي معلومة
يمكن رمي الحالة يمكن الإتلاف وإعادة البناء، أو معاملتها غير منعكسة
الحالة المشتركة محفوظة التلوّث لا يمتد إلى وظائف أخرى
يمكن شرح الآثار الجانبيّة الخارجيّة أُرسل / لم يُرسل / يجوز إعادة الإرسال، معلوم
يمكن إخبار المستخدم بصدق يمكن عرض «هذه المعالجة فشلت»
يمكن المراقبة يمكن تتبّع لاحق بسجلّ أو مقاييس أو dump

6. شروط أفضليّة الإنهاء

بالمقابل، إن انطبق ما يلي فمل إلى الإنهاء.

  • لا يُعرف ما الذي تغيّر جزئياً
  • لُمست حالة مشتركة قابلة للكتابة، والاتّساق غير مقروء
  • انكسر تدبير عمر القفل أو الطابور أو الخيط أو حلقة المراقبة
  • لا يمكن شرح تكرار / نقص / نقصان أثر جانبي خارجي
  • فشل في معالجة الإقلاع أو تهيئة بنية تحتيّة محوريّة
  • اشتباه بحدود أصليّة أو تلف ذاكرة

عند هذا المستوى، تيسير السقوط والاسترداد أنفع من حيلة استمرار نظيف.

الحيلة التي تنفع عند الميل إلى الإنهاءمخطّط يبيّن أنه عند انطباق شروط الميل إلى الإنهاء، تنفع حيلة تيسير السقوط والاسترداد أكثر من حيلة استمرار نظيف.تنطبق شروط الميل إلى الإنهاءحيلة استمرار نظيفحيلة تيسير السقوط والاستردادعند هذا المستوى تنفع قليلاًهذه أنفع

الشكل 11: في وضع الميل إلى الإنهاء، استثمر في سهولة الاسترداد لا في حيلة الاستمرار.

7. توصيات حسب الأنماط الشائعة

جدول 3.1 مكتوب بشكل شروط، فيُحار عند إسقاطه على مشهد فعلي. هذا الجدول إعادة إسقاط لتلك الشروط على مشاهد شائعة. إن حرت، افحص الشرط أولاً في 3.1 ثم ابحث عن الصف القريب في هذا الجدول، فهذا الترتيب أسرع.

استخدام الجدولين عند الحيرةمخطّط يبيّن أنه عند الحيرة في إسقاط المشهد، افحص الشرط أولاً في جدول قرار 3.1 ثم ابحث عن صف المشهد القريب في جدول هذا الفصل.حيرة في إسقاط المشهدافحص الشرط في جدول 3.1ابحث عن الصف القريب في جدول هذا الفصل

الشكل 12: ابدأ بجدول الشروط ثم جدول المشاهد حتى تقلّ الحيرة.

النمط التوصية السبب
زر فتح ملف حدّد مساراً غير موجود استمر مع فشل تلك العملية وحدها تلف الحالة محلّي
سطر واحد تالف في استيراد CSV استمر مع فشل سطر أو ملف واحد يسهل حصر وحدة الفشل
NullReferenceException غير متوقّع في منتصف حفظ الشاشة إعادة إنشاء الشاشة إلى الميل إلى الإنهاء مدى تغيّر ViewModel / حالة العمل مشبوه
رسالة واحدة في الطابور خالفت قاعدة عمل استمر مع فشل تلك الرسالة وحدها يمكن تهريبها إلى طابور عزل
سقطت الحلقة الأم لاستهلاك الطابور على استثناء غير متوقّع أميل إلى إنهاء العملية عمر العامل كلّه مكسور
لا تُقرأ إعدادات لازمة عند الإقلاع إنهاء بفشل إقلاع الإقلاع الناقص أخطر
AccessViolationException حول callback في SDK مورّد أميل إلى إنهاء فوري لا يمكن تجاهل احتمال تلف الذاكرة
فشل إرسال قياس عن بعد غير جوهري فقط عطّل تلك الوظيفة وحدها واستمر يمكن فصل الوظيفة الرئيسة عن نطاق العطل

8. أخطاء شائعة

8.1 catch (Exception) يسجّل فقط ويتابع

هذا خطر جدّاً. يخفي السبب ويسهّل إطالة عمر حالة مكسورة.

8.2 محاولة الاسترداد في آخر معالج استثناء غير معالج

AppDomain.UnhandledException و Application.ThreadException و DispatcherUnhandledException مفيدة مكاناً أخيراً للتسجيل، وليست نقطة استرداد سحريّة.

8.3 إعادة محاولة فجّة رغم أثر جانبي خارجي

إن أعدت المحاولة بلا أمان إعادة تنفيذ في أمر جهاز أو إرسال بريد أو فوترة أو نقل ملف أو تحديث قاعدة بيانات، صار حادث التنفيذ المزدوج هو البطل.

إعادة المحاولة الفجّة تجلب تنفيذاً مزدوجاًمخطّط يبيّن أن إعادة المحاولة بلا أمان إعادة تنفيذ في معالجة ذات أثر جانبي خارجي تجعل حادث التنفيذ المزدوج هو البطل.فشلت معالجة ذات أثر جانبي خارجيإعادة محاولة بلا أمان إعادة تنفيذحادث التنفيذ المزدوج يصير البطلأمر جهاز أو فوترة أو إرسال

الشكل 13: إعادة المحاولة بلا أمان إعادة تنفيذ تجلب حادث تنفيذ مزدوج.

8.4 إبقاء الواجهة بعد موت حلقة المراقبة

تطبيق حيّ شكلاً ولا يعمل يتأخّر اكتشافه لأن المستخدم يراه سليماً. يريد المرء أن تلتقط جهة المراقبة أعراض الزومبي في 3.4.

8.5 قول «لا نريد الإسقاط» دون تصميم للإسقاط

إن كنت لا تريد الإسقاط، فهناك ما يُوضع قبله.

  • إعادة تشغيل تلقائي
  • استرداد الجلسة
  • حفظ نتائج جزئية
  • أمان إعادة التنفيذ
  • فصل نطاق العطل

9. نقاط ترتيب عند التنفيذ

9.1 قرّب مواضع catch إلى الحدود

بدل catch لكل شيء في طبقة عميقة، استقبل عند مواضع يمكن تعريف وحدة الفشل فيها:

  • حدّ عملية واجهة
  • حدّ طلب واحد
  • حدّ وظيفة واحدة
  • حدّ اتّصال واحد
  • حدّ عملية

مثلاً في استيراد يُعالج واحدة واحدة، ضع catch داخل الحلقة. هناك وحدة الفشل.

// C# / .NET 8。1 件ずつ処理するループで、失敗単位をループの内側に閉じ込める例。
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

public sealed record ImportItem(string Id, string Payload);
public sealed record ImportFailure(string Id, string ExceptionType, string Message);
public sealed record ImportSummary(int Succeeded, IReadOnlyList<ImportFailure> Failed);

public interface IImportStore
{
    /// <summary>
    /// فشل قطعة واحدة (خطأ تحقّق، تكرار، صيغة غير صحيحة وغيرها) يُرمى كـ ImportItemException.
    /// غير ذلك ليس «مشكلة هذه القطعة الواحدة» فيُترك ليخرج كما هو.
    /// </summary>
    Task SaveAsync(ImportItem item, CancellationToken cancellationToken);
}

/// <summary>فشل قطعة واحدة يجوز طرحه والمتابعة إلى التالية.</summary>
public sealed class ImportItemException(string message, Exception? inner = null)
    : Exception(message, inner);

public sealed class ImportRunner(IImportStore store, ILogger<ImportRunner> logger)
{
    public async Task<ImportSummary> RunAsync(
        IReadOnlyList<ImportItem> items,
        CancellationToken cancellationToken)
    {
        int succeeded = 0;
        List<ImportFailure> failed = [];

        foreach (ImportItem item in items)
        {
            cancellationToken.ThrowIfCancellationRequested();

            try
            {
                await store.SaveAsync(item, cancellationToken);
                succeeded++;
            }
            catch (ImportItemException ex)
            {
                // حالة القطعة الواحدة تُطرح، لذا سجِّل وتابع إلى القطعة التالية.
                //
                // لا تجعل هذا catch (Exception). إن ابتلعت حتى NullReferenceException
                // و OutOfMemoryException كـ «بيانات سيئة فحسب»، لن تصل إلى الحلقة الأم
                // الاستثناءات غير المتوقَّعة التي قرّرت في 4.3 «إيقاف المضيف كلّه».
                // وبعد أن تصبح الحالة غير موثوقة ستواصل كتابة القطع المتبقية
                logger.LogError(ex, "Import failed. ItemId={ItemId}", item.Id);
                failed.Add(new ImportFailure(item.Id, ex.GetType().Name, ex.Message));
            }
        }

        return new ImportSummary(succeeded, failed);
    }
}

المقابل: لا تبتلع في جانب الحلقة الأم التي تدير هذه المعالجة. كما في 4.3، أسوأ شكل هو موت الحلقة الأم وبقاء العملية، لذلك اربط الاستثناء غير المتوقّع بإيقاف المضيف.

// 常駐ループ側。停止要求は正常系として抜け、それ以外の想定外例外はホストごと止める。
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

public interface IImportQueue
{
    Task<IReadOnlyList<ImportItem>> DequeueBatchAsync(CancellationToken cancellationToken);
}

public sealed class ImportWorker(
    IImportQueue queue,
    ImportRunner runner,
    IHostApplicationLifetime lifetime,
    ILogger<ImportWorker> logger) : BackgroundService
{
    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        try
        {
            using PeriodicTimer timer = new(TimeSpan.FromSeconds(10));

            while (await timer.WaitForNextTickAsync(stoppingToken))
            {
                IReadOnlyList<ImportItem> batch = await queue.DequeueBatchAsync(stoppingToken);
                ImportSummary summary = await runner.RunAsync(batch, stoppingToken);

                logger.LogInformation(
                    "Batch finished. Succeeded={Succeeded} Failed={Failed}",
                    summary.Succeeded,
                    summary.Failed.Count);
            }
        }
        catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
        {
            // طلب الإيقاف مسار طبيعي. انتهِ بهدوء هنا.
        }
        catch (Exception ex)
        {
            // تعطّل حلقة الأب = تعطّل عمر العامل كلّه.
            // لا تبتلع الاستثناء فتبقى العملية حيّة فقط؛ أوقفها وسلّم لإعادة التشغيل.
            logger.LogCritical(ex, "Worker loop failed unexpectedly. Stopping the host.");

            // StopApplication طلب طيّ طبيعي. إن اكتفيت به، لا يميّز
            // المراقب الذي يعيد التشغيل عند الفشل (استرداد الخدمة،
            // systemd Restart=on-failure، سياسة restart للحاوية)
            // الفشل عن «أنهى العمل وخرج طبيعياً»، فلا يستيقظ أبداً.
            //
            // ضبط Environment.ExitCode وحده لا يكفي. عند التشغيل
            // كخدمة Windows، إن أوقف المضيف وقفاً طبيعياً يُبلَّغ SCM
            // بـ SERVICE_STOPPED ولا ينعكس ExitCode في حالة إنهاء الخدمة،
            // فلا تجري عملية الاسترداد. أنهِ العملية برمز خروج غير 0
            Environment.Exit(1);
        }
    }
}

موضع استخدام Environment.Exit(1) هو الجوهر. IHostApplicationLifetime.StopApplication() طلب إيقاف طبيعي، فإن شغّلت كخدمة Windows يُبلَّغ SCM بـ SERVICE_STOPPED. Environment.ExitCode للعملية لا ينعكس في حالة إنهاء الخدمة، لذلك إجراءات الاسترداد المضبوطة في خصائص الخدمة («إعادة تشغيل الخدمة») لا تعمل. درس خدمة العامل الرسمي ينصّ أيضاً على أن السلوك الافتراضي BackgroundServiceExceptionBehavior.StopHost «يوقف بنظافة» فلا تعيد جهة إدارة خدمات Windows التشغيل، وأن تشغيل إجراءات الاسترداد يحتاج استدعاء Environment.Exit برمز خروج غير 0 (المراجع في الفصل 11).

Environment.Exit يُنهي العملية الحالية، لذلك اكتب السجلات التي تريد إخراجها قبل السقوط قبل هذا السطر. إن كان المسجّل يخزّن مؤقّتاً، أدخل تفريغاً. بالمقابل، إن كان الخصم systemd أو حاوية فقط لا خدمة Windows، يكفي ضبط Environment.ExitCode ثم الطي بـ StopApplication() لتعامله جهة المراقبة فشلاً. اختر بحسب البيئة التي تشغّل عليها.

لاحظ أيضاً أن دور catch يتغيّر بين داخل الحلقة وخارجها. الداخل تسجيل وحدة الفشل، والخارج إنهاء العمر. إن عكست، سقط التطبيق بفشل واحدة، أو بقي العملية بعد موت العامل.

دور catch داخل الحلقة وخارجهامخطّط يبيّن أن catch داخل الحلقة لتسجيل وحدة الفشل وcatch خارجها لإنهاء العمر، وأن العكس يسقط بفشل واحدة أو يُبقي العملية بعد موت العامل.catch داخل الحلقةتسجيل وحدة الفشلcatch خارج الحلقةإنهاء العمرالعكس يسقط بفشل واحدةالعكس يُبقي العملية وحدها

الشكل 14: دور catch يختلف داخل الحلقة وخارجها، والعكس حادث في الاتجاهين.

9.2 افصل الاستثناء المتوقّع عن غير المتوقّع

  • متوقّع: validation، not found، timeout، cancel، مخالفة قاعدة عمل
  • غير متوقّع: انكسار افتراض، تسرّب من الحلقة الأم، خلل حدود أصليّة، رائحة تلف ذاكرة

9.3 صغّر الحالة المشتركة

كلّما كبرت الحالة المشتركة القابلة للكتابة صعب حكم الاستمرار. وبالعكس، كلّما حُصرت داخل شاشة واحدة وجلسة واحدة وعامل واحد سهل حصر الفشل أيضاً.

9.4 أخرج المعالجة الخطرة إلى عملية أخرى

COM / ActiveX / SDK مورّد / unsafe / معالجة صور ثقيلة / تحكّم جهاز خارجي: ما لا تريد توسيع ضرر سقوطه، فصله في عملية أخرى ينفع كثيراً.

9.5 معالج الاستثناء غير المعالج «تسجيل» لا «استرداد»

  • معلومات الاستثناء
  • سياق العملية
  • السجلات المهمّة السابقة
  • الإعداد / الإصدار / جهة الاتّصال
  • مسار أخذ dump

رتّب هذا حتى يمكن التحقيق بعد السقوط، فالنتيجة أكثر استقراراً.

في WPF يكفي معالج تسجيل بهذا الشكل تقريباً.

// WPF (.NET 8) の App.xaml.cs。ハンドラーは「回復」ではなく「記録」に使う。
using System;
using System.IO;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;

namespace SampleApp;

public partial class App : Application
{
    private static readonly string CrashLogPath = Path.Combine(
        Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
        "SampleApp",
        "crash.log");

    protected override void OnStartup(StartupEventArgs e)
    {
        // ハンドラーの登録は base.OnStartup(e) より前に置く。
        // base.OnStartup は Startup イベントを発火させるので、その購読側で例外が
        // 出ると、後ろに書いたハンドラーはまだ登録されておらず、
        // 「起動時に落ちたのにログが1行も残らない」という一番困る形になる。
        // 起動処理そのものの失敗こそ記録したいので、先に張っておく。

        // UI スレッドで未処理のまま上がってきた例外
        DispatcherUnhandledException += (_, args) =>
        {
            Record("DispatcherUnhandledException", args.Exception);

            // args.Handled = true にすれば続行できるが、続けてよいかは 5 章の条件で判断する。
            // 判断がつかないなら、記録して既定動作(終了)に任せる。
            args.Handled = false;
        };

        // UI スレッド以外も含む最後の通知。ここでは止められないので記録専用。
        AppDomain.CurrentDomain.UnhandledException += (_, args) =>
            Record("AppDomain.UnhandledException", args.ExceptionObject as Exception);

        // await されないまま回収された Task の例外
        TaskScheduler.UnobservedTaskException += (_, args) =>
        {
            Record("UnobservedTaskException", args.Exception);
            args.SetObserved();
        };

        // ここまで張り終えてから、既定の起動処理(Startup イベントの発火)へ進む
        base.OnStartup(e);
    }

    private static void Record(string source, Exception? exception)
    {
        try
        {
            Directory.CreateDirectory(Path.GetDirectoryName(CrashLogPath)!);

            string version = Assembly.GetExecutingAssembly().GetName().Version?.ToString() ?? "unknown";
            string text = string.Join(
                Environment.NewLine,
                $"[{DateTimeOffset.Now:O}] {source}",
                $"version={version} os={Environment.OSVersion} user={Environment.UserName}",
                exception?.ToString() ?? "(no exception object)",
                string.Empty);

            File.AppendAllText(CrashLogPath, text);
        }
        catch
        {
            // 記録に失敗しても、終了処理を止めない。
        }
    }
}

الجوهر: لا تحاول إصلاح الحالة داخل المعالج. ما يُفعل هنا ترك مواد تتيح تتبّع الظاهرة نفسها لاحقاً فقط.

مسار معالج التسجيل فقطمخطّط يبيّن أنه في WPF تُسجَّل المعالجات قبل base.OnStartup ثم تُستأنف معالجة الإقلاع، وأن داخل المعالج لا تُصلح الحالة بل تُترك مواد تتيح تتبّع الظاهرة نفسها لاحقاً فقط.سجّل المعالجات أولاًانتقل إلى base.OnStartupسجّل الاستثناء غير المعالجلا تحاول إصلاح الحالة

الشكل 15: أنهِ التسجيل قبل معالجة الإقلاع، واجعل المعالج تسجيلاً فقط.

9.6 لا تفرط في الثقة بأحداث الاستثناء غير المعالج في WPF / WinForms

في WPF، Handled = true في DispatcherUnhandledException يسمح بالمتابعة بعد استثناء غير معالج بذاتها. وفي Windows Forms أيضاً يمكن اختيار طريقة التوقّف على خيط الواجهة الرئيسي حسب Application.ThreadException و SetUnhandledExceptionMode.

غير أن إمكان المتابعة بذلك واكتمال شروط الاسترداد مسألتان مختلفتان.

9.7 Environment.FailFast فقط عندما «التنظيف أخطر»

FailFast في مخطّط تدفق 3.1 هو Environment.FailFast. السلوك المكتوب في الوثائق الرسمية كالتالي.

  • يُنهي العملية دون تشغيل try / finally الجاري ولا المنهي
  • على Windows يكتب الرسالة الممرَّرة إلى سجل أحداث تطبيقات Windows وينشئ dump للتطبيق ثم يُنهي
  • الرسالة ومعلومات الاستثناء تُدرج أيضاً في تقرير خطأ إلى Microsoft عبر Windows Error Reporting
  • الاستدعاء تحت مُنقّح Visual Studio يصير ExecutionEngineException ويظهر managed debugging assistant باسم fatalExecutionEngineError

عدم تشغيل finally ليس عيباً، بل غرض هذه الواجهة. تشغيل كود تنظيف وحالة مكسورة قد يكتب المحتوى المكسور كما هو إلى ملف أو قاعدة بيانات. الوثائق نفسها تقول استخدم FailFast لا Environment.Exit عندما تكون حالة التطبيق مكسورة بما لا يُصلح، وتشغيل try / finally أو المنهي سيُتلف موارد.

الفصل كالتالي.

الوضع ما تختاره
الشرط الثابت مكسور، وتشغيل التنظيف أخطر Environment.FailFast
الحالة سليمة وتريد الإنهاء بعد التنظيف إنهاء طبيعي (IHostApplicationLifetime.StopApplication ونحوه)
تريد فقط إرجاع رمز خروج والانتهاء Environment.Exit أو return من Main

في الكود يُستخدم في موضع كهذا.

// C# / .NET 8。共有状態の不変条件が壊れたことを検出した箇所。
// ここから先は、どの後片づけ処理も信用できない。
if (cache.Count != store.Count)
{
    Environment.FailFast(
        $"Invariant broken: cache={cache.Count} store={store.Count}",
        new InvalidOperationException("Cache and store are out of sync."));
}

يُؤخذ dump تلقائياً، لذلك ضع في رسالة FailFast أي شرط ثابت انكسر وبأي قيم. بالمقابل، استدعاء FailFast لفشل متوقّع مثل خطأ إدخال أو خطأ اتّصال مبالغ فيه. هناك عد إلى حديث وحدة الفشل في 9.1.

سبب عدم تشغيل FailFast للتنظيفمخطّط يبيّن أنه إن شُغّل كود تنظيف وحالة غير قابلة للإصلاح فقد يُكتب المحتوى المكسور، لذلك يُنهي Environment.FailFast دون تشغيل try أو finally ولا المنهي، ويترك على Windows سجل أحداث وdump.انكسر الشرط الثابتإنهاء فوري بـ FailFastلا يُشغَّل finally ولا المنهييُترك dump وسجل أحداث (Windows)يُمنع كتابة المحتوى المكسور

الشكل 16: FailFast يمنع كتابة الحالة المكسورة بعدم تشغيل التنظيف.

10. الخلاصة

ما يُنظر إليه عند وقوع استثناء غير متوقّع ليس «هل يمكن catch لهذا الاستثناء»، بل هل ما زال يمكن الوثوق بحالة التطبيق بعدها.

ترتيب الحكم يكفي تقريباً بهذا.

  1. هل يمكن رمي وحدة الفشل
  2. هل يمكن إعادة الحالة المشتركة أو إعادة بنائها
  3. هل يمكن شرح الآثار الجانبيّة الخارجيّة
  4. هل يمكن الوثوق بسلامة الذاكرة / الخيوط / الحدود الأصليّة

إن وثقت بالأربعة يمكن الاستمرار. إن لم تثق، مل إلى الإنهاء.

ترتيب الحكم في الخلاصةمخطّط يبيّن مسار الحكم بفحص إمكان رمي وحدة الفشل، ثم إعادة الحالة المشتركة، ثم شرح الآثار الجانبيّة الخارجيّة، ثم الوثوق بالسلامة بما فيها الحدود الأصليّة، والاستمرار إن وُثق بالأربعة وإلا الميل إلى الإنهاء.وثقت بالأربعةلا ثقةهل يمكن رمي وحدة الفشلهل يمكن إعادة الحالة المشتركةهل يمكن شرح الآثار الجانبيّة الخارجيّةهل يمكن الوثوق بالسلامةيمكن الاستمرارأميل إلى الإنهاء

الشكل 17: أجب عن الأسئلة الأربعة بالترتيب، واختر الاستمرار فقط عندما تثق بها جميعاً.

خصوصاً في تطبيق طويل التشغيل، وتطبيق مراقبة، وخدمة، وربط أجهزة، البقاء حيّاً وهو مكسور أخطر كثيراً في مواضع من السقوط بصراحة.

معالجة الاستثناء ليست «تقنية عدم الإسقاط». هي تصميم يصغّر شكل الكسر، ويتوقّف بصدق إن انكسر، ويسهّل الاسترداد.

روابط مرجعية

أحدث المقالات التي تشترك في نفس الوسوم. عمّق فهمك بمواضيع مرتبطة.

ترتبط هذه المقالة بشكل طبيعي بصفحات الخدمات التالية.

الأسئلة الشائعة

أسئلة شائعة حول موضوع هذه المقالة.

هل يُمنع ابتلاع استثناء غير متوقّع بـ catch (Exception) ومتابعة العمل؟
تسجيل السطر ومتابعة العمل خطر في الغالب. يخفي السبب ويسهّل إطالة عمر حالة مكسورة. الاستمرار جائز فقط عندما تجتمع ثلاثة: يمكن رمي وحدة الفشل، يمكن إعادة الحالة المشتركة، يمكن شرح الآثار الجانبيّة الخارجيّة. محور الحكم ليس «هل يمكن catch لهذا الاستثناء» بل «هل ما زال يمكن الوثوق بحالة التطبيق بعدها».
أي استثناء يستدعي الإنهاء فوراً؟
استثناءات تشكّك في سلامة العملية كلّها مثل StackOverflowException وAccessViolationException وOutOfMemoryException الخطير لا يُبنى عليها افتراض الاستمرار. StackOverflowException يعني انهيار مكدّس الاستدعاء، وAccessViolationException وصول غير قانوني إلى ذاكرة محميّة فيُشتبه بتلف الذاكرة. الخلل الناشئ من callback في COM أو P/Invoke أو SDK مورّد يصعب الحكم على سلامته من الجانب المُدار وحده، فيميل بقوّة إلى الإنهاء.
ما شروط جواز استمرار التطبيق بعد الاستثناء؟
يفترض أن تجتمع تقريباً: وحدة فشل واضحة (عملية واحدة، شاشة واحدة، وظيفة واحدة، اتّصال واحد يمكن رميه)، يمكن إتلاف الحالة وإعادة بنائها، التلوّث لا يمتد إلى الحالة المشتركة، يمكن شرح الآثار الجانبيّة الخارجيّة، يمكن إخبار المستخدم بصدق أن «هذه المعالجة فشلت»، ويمكن تتبّع لاحق بسجلّ أو مقاييس. إن كان حدّ المعالجة واضحاً مثل عملية واجهة واحدة أو وظيفة استيراد واحدة فقد يمكن الاستمرار. بالمقابل، التحديث في منتصف حالة مشتركة، والحلقة الأم، ومعالجة الإقلاع، والخلل عند الحدود الأصليّة تميل إلى الإنهاء.
هل يكفي Handled=true في DispatcherUnhandledException لـ WPF للاستمرار؟
الاستمرار بعد استثناء غير معالج ممكن بذاته، لكن إمكان الاستمرار وسلامة الاستمرار مسألتان مختلفتان. معالجات مثل AppDomain.UnhandledException وDispatcherUnhandledException مفيدة مكاناً أخيراً للتسجيل، وليست نقطة استرداد سحريّة. ترتيب معلومات الاستثناء وسياق العملية ومسار أخذ dump حتى يمكن التحقيق بعد السقوط أكثر استقراراً في النتيجة. خصوصاً في خدمة طويلة التشغيل أو تطبيق مراقبة، السقوط وإعادة التشغيل غالباً أأمن وأسهل تشخيصاً من البقاء نصف مكسور.

الملف الشخصي للمؤلف

صفحة الملف الشخصي لمؤلف المقالة.

غو كومورا

مؤسّس شركة كومورا سوفت ذ.م.م.

يركّز على تطوير برامج ويندوز، والاستشارات التقنية، والتحقيق في الأخطاء، ويتميّز في المشاريع التي تبقى فيها الأصول القديمة ناشطة، وفي تشخيص الأعطال التي يصعب تحديد سببها.

العودة إلى المدونة