finally#
מריץ קוד ניקוי בדרך החוצה מ־try/catch, בין אם הגוף הצליח, השליך, או קפץ הצידה.
finally מציג בלוק שלישי שיכול לעקוב אחר זוג try/catch. גופו רץ פעם אחת בדיוק, לאחר שגוף ה־try משלים - כיצד שהוא לא משלים. השלמה רגילה, חריגה שנתפסה על־ידי ה־catch, חריגה שבורחת כי אין catch תואם, return מתת־השגרה הסובבת, goto, last/next/redo היוצא מהלולאה הסובבת: כל אחד מאלה מנותב דרך בלוק ה־finally לפני שהבקרה ממשיכה. הדפוס שקול לתזמון בלוק defer בראש ה־try, עם תחביר השומר על הניקוי מחובר ויזואלית ל־try/catch אליו הוא שייך.
תקציר#
use feature 'try';
no warnings 'experimental::try';
try { BODY }
catch ($e) { HANDLE }
finally { CLEANUP }
מה מקבלים בחזרה#
ל־finally אין ערך החזרה. הביטוי הסופי של הבלוק מוערך לתופעות הלוואי שלו ואז מושלך - הוא אינו תורם לערך של ביטוי ה־try/catch הסובב, גם אם המבנה כולו הוא ההצהרה האחרונה של תת־שגרה או בלוק do המשמש להפקת ערך.
my $v = do {
try { compute() }
catch ($e) { $DEFAULT }
finally { log("done"); 999 } # 999 is discarded
};
# $v is either compute()'s value or $DEFAULT
השתמשו ב־finally עבור ניקוי שחייב להתרחש, לא לחישוב תוצאה.
סטטוס התכונה#
finally הוא חלק מתכונת ה־try, אבל בניגוד ל־try ול־catch (שהפכו לבלתי־ניסיוניים ב־Perl 5.40) הוא נשאר ניסיוני ב־Perl 5.42. שימוש בו ללא השתקת האזהרה מפיק:
finally is experimental at ... line N.
הפעלת התכונה והשתקת האזהרה יחד:
use feature 'try';
no warnings 'experimental::try';
מכיוון שקטגוריית האזהרה היא experimental::try - אותה הקטגוריה ש־try/catch נהגו להפיק - השתקתה מאפשרת את finally מבלי להפעיל מחדש אזהרות על החלקים היציבים כעת של התכונה שעבורם כן רוצים אבחונים.
מתי הבלוק רץ#
החוזה הוא ״בדרך החוצה, ללא תלות באופן״. באופן קונקרטי, finally רץ לאחר כל אחד מהבאים:
השלמה רגילה של
try. גוף ה־tryנפל מהקצה או החזיר את ערך הביטוי האחרון שלו;finallyרץ הבא, ואז ההצהרה שלאחר מבנה ה־try.חריגה שנתפסה על־ידי
catch. גוף ה־tryהשליך, ה־catchהתאים וטיפל בה;finallyרץ לאחר גוף ה־catch.חריגה שלא נתפסה. אין
catch, או שה־catchעצמו השליך מחדש עםdie;finallyעדיין רץ, ואז החריגה ממשיכה להתפשט ל־eval/tryהדינמי הבא כלפי חוץ.returnבתוךtryאוcatch. תת־השגרה הסובבת עומדת לחזור;finallyרץ, ואז ה־return נכנס לתוקף.בקרת לולאה (
last,next,redo) בתוךtryאוcatch.finallyרץ, ואז בקרת הלולאה נכנסת לתוקף בלולאה הסובבת.goto &subאוgoto LABELהיוצא מה־try.finallyרץ לפני שהקפיצה משלימה.
הסדר חשוב: אם ה־catch עצמו מעלה חריגה חדשה, finally עדיין רץ, והחריגה החדשה היא זו שמתפשטת (המקורית מוחלפת, בדיוק כמו בכל die בתוך מטפל).
הגבלות על גוף הבלוק#
בלוק finally הוא נקודת ניקוי, לא הפנייה של זרימת בקרה. Perl דוחה כל הצהרת זרימת־בקרה שתנסה לדרוס את האופן שבו המבנה כבר מתפרק:
returnבתוךfinallyהיא שגיאת תחביר / שגיאת זמן־הידור.gotoבתוךfinallyהיא שגיאת תחביר / שגיאת זמן־הידור.בקרות לולאה (
last,next,redo) הממוקדות בלולאה מחוץ לבלוק ה־finallyנדחות.
בתוך לולאה רגילה הכלולה בגוף ה־finally, בקרות הלולאה חלות על אותה לולאה פנימית כרגיל - ההגבלה היא על יציאה מהבלוק finally, לא על לולאות בתוכו.
die מותר. בלוק finally שמשליך מחליף כל חריגה שבמהלך טיסה מה־try/catch בחדשה. זה בדרך כלל באג (מאבדים את הכישלון המקורי), אז שמרו על קוד ניקוי שעלול בעצמו להיכשל:
finally {
eval { $fh->close }; # swallow close errors during cleanup
}
היחס ל־defer#
finally BLOCK הוא ״defer BLOCK המוצב בראש ה־try, מחובר במקום זאת לזוג try/catch.״ שניהם רצים בכל מסלול יציאה מהתחום הסובב שלהם; שניהם אוסרים על אותן הצהרות זרימת בקרה בגופם; שניהם משליכים את ערך ההחזרה שלהם.
ההבדל הוא בהצמדת התחום:
deferנצמד לבלוק הסובב הפנימי ביותר ויורה ביציאה מאותו בלוק. זהו פרימיטיב הניקוי לשימוש כללי ועובד בכל בלוק.finallyנצמד למבנה ה־try/catchויורה כאשר אותו מבנה סיים. זה סוכר תחבירי למקרה הנפוץ שבו הניקוי הוא ספציפית על מה שה־tryעשה.
# These two are behaviourally equivalent:
try {
my $fh = acquire();
defer { release($fh) }
work($fh);
}
catch ($e) { warn $e }
try {
my $fh = acquire();
work($fh);
}
catch ($e) { warn $e }
finally { release_last() } # if $fh were available here
שימו לב לתוצאה מעשית אחת: משתנים שהוצהרו בתוך גוף ה־try יוצאים מהתחום לפני ש־finally רץ. אם הניקוי זקוק למשאב שהוקצה בתוך ה־try, או שתצהירו עליו בתחום חיצוני, או שתשתמשו ב־defer בתוך ה־try היכן שהמשתנה חי.
דוגמאות#
לסגור תמיד מטפל, גם בשגיאה:
use feature 'try';
no warnings 'experimental::try';
open my $fh, '<', $path or die "open $path: $!";
try {
process($fh);
}
catch ($e) {
warn "processing failed: $e";
}
finally {
close $fh;
}
לשחרר מנעול ללא תלות בתוצאה:
my $lock = $mutex->lock;
try {
critical_section();
}
finally {
$lock->release;
}
תצפיתיות: לתעד כל מסלול יציאה:
try {
run($job);
}
catch ($e) {
$job->mark_failed($e);
die $e; # re-throw; finally still runs
}
finally {
metrics->tick('job.exit', $job->id);
}
try/catch מפיק־ערך, עם finally לניקוי בלבד:
my $result = do {
try { fetch($url) }
catch ($e) { $CACHED }
finally { close_connection() }
};
# $result is fetch() or $CACHED; finally's value is never used
מקרי קצה#
ניתן להשמיט את
catch.try { ... } finally { ... }חוקי ומריץ אתfinallyהן ביציאה רגילה והן ביציאת חריגה; החריגה אז מתפשטת כאילו לא היה שםtry.try/finallyמקוננים: בלוקיfinallyיורים פנימי־ראשון ככל שהתחומים מתפרקים, תואם את סמנטיקתdefer.חריגה בתוך
finally: מחליפה כל חריגה שבמהלך טיסה. המקורית אובדת אלא אם לכדתם אותה ב־catchקודם.$@במהלךfinally: לא אמין עבור ״מה זה עתה קרה״. אם ה־finallyזקוק לדעת האם הוא רץ לאחר הצלחה או כישלון, קבעו דגל בבלוק ה־catchובדקו אותו:my $failed; try { risky() } catch ($e) { $failed = $e; die $e } finally { if ($failed) { rollback() } else { commit() } }
הריסה גלובלית: בלוקי
finallyהרשומים על־ידיtryשעדיין פעיל כאשר המפרש מתחיל לפרק את עצמו רצים, אך אותן הסתייגויות כמוdieבתוךDESTROYחלות - שמרו את הגוף מגוחן.הפירוק דורש את התכונה: ללא
use feature 'try', המילהfinallyאינה מילת מפתח ומפורקת כקריאת פונקצייה של bareword, מה שמפיק בדרך כלל שגיאה מבלבלת לא־קשורה. תמיד הפעילו את התכונה לפני שימוש בתחביר.
הבדלים מהמקור#
תאימות מלאה עם Perl 5.42 המקורי.
ראו גם#
try- מציג את הבלוק שאת יציאתוfinallyמתחבר; ראו Try Catch Exception Handling ב־perlsyncatch- הבלוק האמצעי האופציונלי המטפל בחריגות שהושלכו על־ידיtry;finallyרץ אחריו בכל מקרהdefer- הבלוק הכללי ״הרץ ביציאת תחום״;finallyהואdeferמחובר לזוגtry/catchdie- הדרך היחידה לגרום לענף ה־catchלירות;dieבתוךfinallyמחליף כל חריגה שבמהלך טיסהeval- המנגנון הישן יותר לתפיסת חריגות;try/catch/finallyמחליפים את הניב הנפוץeval { ... }; if ($@)עם תיחום נקי יותר וסמנטיקת זרימת־בקרה נכונה