سؤال

بناء الجملة جانبا، ما هو الفرق بين

try {
}
catch() {
}
finally {
    x = 3;
}

و

try {
}
catch() {
}

x = 3;

يحرر:في .NET 2.0؟


لذا

try {
    throw something maybe
    x = 3
}
catch (...) {
    x = 3
}

هو يعادل سلوكيا؟

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

المحلول

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

في المثال الثاني، إذا عاد الكود الموجود في كتلة الالتقاط أو تم إنهاؤه، فلن يتم تنفيذ x = 3.في الأول سوف.

في النظام الأساسي .NET، في بعض الحالات لن يتم تنفيذ الكتلة النهائية:استثناءات الأمان، تعليق سلسلة المحادثات، إيقاف تشغيل الكمبيوتر :)، وما إلى ذلك.

نصائح أخرى

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

في جافا:

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

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

إذا جربت هذا المثال:

try {
  return 0;
} finally {
  return 2;
}

ستكون النتيجة 2 :)

المقارنة مع اللغات الأخرى: العودة من النهاية

هناك العديد من الأشياء التي تجعل الكتلة النهائية مفيدة:

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

هذه تجعل الكتل أخيرًا ممتازة لإغلاق مقابض الملفات أو المقابس.

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

إذا قمت، على سبيل المثال، بإلقاء استثناء جديد في كتلة الالتقاط (إعادة الرمي)، فلن يتم تنفيذ المهمة إلا إذا كانت في الكتلة النهائية.

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

يجب ألا تستخدم أبدًا عبارات التحكم (return،break، continue) في النهاية، لأن هذا يمكن أن يكون كابوسًا للصيانة وبالتالي يعتبر ممارسة سيئة

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

@iAn و @mats:

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

StreamReader stream = new StreamReader("foo.bar");  
try {
    mySendSomethingToStream(stream);
}
catch(noSomethingToSendException e) {
    //Swallow this    
    logger.error(e.getMessage());
}
catch(anotherTypeOfException e) {
    //More serious, throw this one back
    throw(e);
}
finally {
    stream.close();  
}

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

من المفترض أن يتم تنفيذ الكتلة الأخيرة سواء اكتشفت الاستثناء أم لا.يرى جرب / التقط / أخيرًا مثال

@إد, ، ربما تفكر في شيء مثل catch(...) الذي يمسك استثناء غير محدد في C++.

لكن finally هو الكود الذي سيتم تنفيذه بغض النظر عما يحدث في ملف catch كتل.

مايكروسوفت لديها صفحة مساعدة على حاول أخيرًا لـ C#

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

تخيل أن لديك معالج ملفات، وهذا هو الفرق في كيفية كتابته.

try
{
   StreamReader stream = new StreamReader("foo.bar");
   stream.write("foo");
}
catch(Exception e) { } // ignore for now
finally
{
   stream.close();
}

مقارنة ب

StreamReader stream = null;
try
{
    stream = new StreamReader("foo.bar");
    stream.write("foo");
} catch(Exception e) {} // ignore

if (stream != null)
    stream.close();

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

يتم تشغيل أي كود في النهاية حتى في حالة وجود استثناء غير معالج.عادةً ما يتم استخدام التعليمة البرمجية النهائية لتنظيف الإعلانات المحلية للتعليمات البرمجية غير المُدارة باستخدام .dispose().

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

@mats محق جدًا في أن هناك دائمًا احتمال حدوث حالات فشل "صعبة" - وأخيرًا، لا ينبغي أن تتضمن الكتل تعليمات برمجية مهمة، والتي يجب أن تتم دائمًا من خلال المعاملات داخل المحاولة{}

@mats مرة أخرى - الجمال الحقيقي هو أنه يسمح لك بإلغاء الاستثناءات من أساليبك الخاصة، مع ضمان قيامك بالترتيب:

try
{
StreamReader stream = new StreamReader("foo.bar");
mySendSomethingToStream(stream);
}
catch(noSomethingToSendException e) {
    //Swallow this    
    logger.error(e.getMessage());
}
catch(anotherTypeOfException e) {
    //More serious, throw this one back
    throw(e);
}
finally
{
stream.close();
}

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

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

على سبيل المثال، إغلاق جلسة قاعدة بيانات أو اتصال JMS، أو إلغاء تخصيص بعض موارد نظام التشغيل.

أعتقد أنه مشابه في .NET؟

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