هل من السيء حقًا الحصول على استثناء عام؟

StackOverflow https://stackoverflow.com/questions/21938

  •  09-06-2019
  •  | 
  •  

سؤال

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

هل كانت مفيدة؟

المحلول

من الواضح أن هذا أحد الأسئلة التي تكون الإجابة الحقيقية الوحيدة فيها هي "الأمر يعتمد".

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

والسبب في ذلك هو أنه على سبيل المثال.لا ترغب في التقاط جميع الاستثناءات في المكتبة لأنك قد تخفي المشكلات التي لا علاقة لها بمكتبتك، مثل "OutOfMemoryException" الذي تفضل حقًا ظهور الفقاعات حتى يمكن إعلام المستخدم، وما إلى ذلك.من ناحية أخرى، إذا كنت تتحدث عن التقاط الاستثناءات داخل طريقة main() الخاصة بك والتي تلتقط الاستثناء، وتعرضه ثم تخرج...حسنًا، ربما يكون من الآمن اكتشاف أي استثناء هنا.

القاعدة الأكثر أهمية حول التقاط جميع الاستثناءات هي أنه لا ينبغي عليك أبدًا ابتلاع جميع الاستثناءات بصمت ...على سبيل المثالشيء من هذا القبيل في جافا:

try { 
    something(); 
} catch (Exception ex) {}

أو هذا في بايثون:

try:
    something()
except:
    pass

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

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

نصائح أخرى

ما لم تكن تقوم ببعض عمليات التسجيل وتنظيف التعليمات البرمجية في الواجهة الأمامية لتطبيقك، فأعتقد أنه من السيئ التقاط جميع الاستثناءات.

قاعدتي الأساسية هي التقاط جميع الاستثناءات التي تتوقعها وأي شيء آخر يعتبر خطأ.

إذا لاحظت كل شيء واستمرت في السير، فإن الأمر يشبه إلى حدٍ ما وضع لاصقة لاصقة على ضوء التحذير الموجود على لوحة القيادة في سيارتك.لا يمكنك رؤيته بعد الآن، لكن هذا لا يعني أن كل شيء على ما يرام.

نعم!(ما عدا في "الجزء العلوي" من التطبيق الخاص بك)

من خلال اكتشاف استثناء والسماح بمواصلة تنفيذ التعليمات البرمجية، فإنك تشير إلى أنك تعرف كيفية التعامل مع مشكلة معينة والتحايل عليها أو إصلاحها.أنت تقول أن هذا هو حالة قابلة للشفاء.يعني التقاط الاستثناء أو SystemException أنك ستكتشف مشكلات مثل أخطاء الإدخال/الإخراج، وأخطاء الشبكة، وأخطاء نفاد الذاكرة، وأخطاء التعليمات البرمجية المفقودة، وإلغاء مرجعية المؤشر الفارغ، وما شابه ذلك.إنها كذبة القول أنه يمكنك التعامل مع هذه الأشياء.

في تطبيق منظم جيدًا، يجب معالجة هذه المشكلات غير القابلة للاسترداد في أعلى المكدس.

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

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

ليس هناك ما هو أسوأ من تعطل التطبيق مع ظهور بعض عمليات التتبع غير الملائمة للمستخدم على الشاشة.فهو لا يعطي نظرة ثاقبة (ربما غير مرغوب فيها) على التعليمات البرمجية الخاصة بك فحسب، بل إنه يربك المستخدم النهائي أيضًا، وأحيانًا يخيفه ويدفعه إلى تطبيق منافس.

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

النقطة ذات شقين على ما أعتقد.

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

ثانيًا، تركز إرشادات FxCop على رمز المكتبة/إطار العمل - ولم يتم تصميم جميع قواعدها بحيث تنطبق على مواقع الويب EXE أو ASP.Net.لذا فإن وجود معالج استثناء عالمي يقوم بتسجيل جميع الاستثناءات والخروج من التطبيق بشكل جيد يعد أمرًا جيدًا.

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

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

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

لذا بشكل عام، يعد اكتشاف الاستثناءات العامة أمرًا سيئًا إلا إذا كنت متأكدًا بنسبة 100% من أنك تعرفها بالضبط ما هي أنواع الاستثناءات التي سيتم طرحها وتحت أي ظروف.إذا كنت في شك، فاسمح لهم بالذهاب إلى معالج الاستثناء ذي المستوى الأعلى بدلاً من ذلك.

هناك قاعدة مماثلة هنا وهي عدم طرح استثناءات من النوع System.Exception.قد ترغب أنت (أو مطور آخر) في التقاط الاستثناء المحدد الخاص بك في أعلى مكدس الاستدعاءات مع السماح للآخرين بالمرور.

(ومع ذلك، هناك نقطة واحدة يجب ملاحظتها.في .NET 2.0، إذا واجه أحد المواضيع أي استثناءات لم يتم اكتشافها، فإنه يؤدي إلى إلغاء تحميل مجال التطبيق بالكامل.لذا يجب عليك تغليف النص الرئيسي لسلسلة المحادثات في كتلة محاولة عامة...تمرير أي استثناءات تم اكتشافها هناك إلى رمز معالجة الاستثناءات العالمي الخاص بك.)

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

في الختام، سوف تلتقط كليهما IOException و NullPointerException مع عام Exception, ، ولكن ربما تكون الطريقة التي يجب أن يتفاعل بها برنامجك مختلفة.

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

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

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

  • الجزء العلوي من جميع سلاسل الرسائل (افتراضيًا، ستختفي الاستثناءات دون أن تترك أي أثر!)
  • داخل حلقة المعالجة الرئيسية التي تتوقع عدم الخروج منها أبدًا
  • داخل حلقة معالجة قائمة بالكائنات التي لا ينبغي لفشل واحد أن يوقف الآخرين
  • الجزء العلوي من الموضوع "الرئيسي"--يمكنك التحكم في العطل هنا، مثل تفريغ القليل من البيانات إلى stdout عند نفاد الذاكرة.
  • إذا كان لديك "Runner" الذي يقوم بتشغيل التعليمات البرمجية (على سبيل المثال، إذا أضاف شخص ما مستمعًا إليك وقمت بالاتصال بالمستمع) فعند تشغيل التعليمات البرمجية، يجب عليك التقاط الاستثناء لتسجيل المشكلة والسماح لك بمواصلة إخطار المستمعين الآخرين.

في هذه الحالات، تريد دائمًا اكتشاف الاستثناء (وربما يكون قابلاً للرمي في بعض الأحيان) من أجل اكتشاف أخطاء البرمجة/غير المتوقعة، وتسجيلها والمتابعة.

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

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

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

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

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

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

لقد قمت للتو بتغيير جميع الاستثناءات إلى استثناء عام، والآن لا داعي للقلق بشأن طرح أي استثناءات عشوائية أخرى غير متوقعة.

قبل:

    catch (final RemoteException exc)
    {
        exc.printStackTrace();
    }
    catch (final IntentSender.SendIntentException exc)
    {
        exc.printStackTrace();
    }
    catch (final IabHelper.IabAsyncInProgressException exc)
    {
        exc.printStackTrace();
    }
    catch (final NullPointerException exc)
    {
        exc.printStackTrace();
    }
    catch (final IllegalStateException exc)
    {
        exc.printStackTrace();
    }

بعد:

    catch (final Exception exc)
    {
        exc.printStackTrace();
    }

رأي غير شعبي:ليس حقيقيًا.

اكتشف جميع الأخطاء التي يمكنك التعافي منها بشكل مفيد.في بعض الأحيان هذا كل منهم.

في تجربتي، الأمر أكثر أهمية أين جاء الاستثناء مما تم طرح الاستثناء بالفعل.إذا احتفظت باستثناءاتك في أماكن ضيقة، فلن تبتلع عادةً أي شيء قد يكون مفيدًا.معظم المعلومات المشفرة على شكل خطأ هي معلومات إضافية، لذلك ينتهي بك الأمر عمومًا إلى اكتشاف الخطأ بشكل فعال الجميع منها على أي حال (ولكن عليك الآن البحث عن مستندات API للحصول على المجموعة الإجمالية من الاستثناءات المحتملة).

ضع في اعتبارك أن بعض الاستثناءات يجب أن ترتفع إلى الأعلى في كل حالة تقريبًا، مثل Python KeyboardInterrupt و SystemExit.لحسن الحظ بالنسبة لبايثون، يتم الاحتفاظ بها في فرع منفصل من التسلسل الهرمي للاستثناءات، بحيث يمكنك السماح لها بالتطوير عن طريق اللحاق Exception.إن التسلسل الهرمي للاستثناءات المصمم جيدًا يجعل هذا النوع من الأشياء واضحًا ومباشرًا.

الوقت الرئيسي الذي سيؤدي فيه اكتشاف الاستثناءات العامة إلى حدوث مشكلات خطيرة هو عند التعامل مع الموارد التي تحتاج إلى التنظيف (ربما في finally جملة)، نظرًا لأن المعالج الشامل يمكن أن يفوتك هذا النوع من الأشياء بسهولة.لحسن الحظ، هذه ليست مشكلة حقًا بالنسبة للغات ذات defer, ، يبني مثل بايثون with, أو RAII في C++ وRust.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top