catch#
מטפל בחריגה שנזרקה על־ידי בלוק try קודם.
catch הוא החצי השני של מבנה try/catch. הוא אינו הוראה עצמאית: הוא בא אחרי בלוק try, מצהיר על משתנה לקסיקלי VAR שיקבל את ערך החריגה, ומריץ את ה־BLOCK שלו רק כאשר בלוק try מת. אם בלוק try הסתיים כרגיל, בלוק catch מדולג. הבקרה מתחדשת אז בהוראה שאחרי המבנה כולו (או בלוק finally, אם כזה קיים).
יש להפעיל תחביר זה עם use feature 'try'. try ו־catch אינם ניסיוניים החל מ־Perl 5.40.
תקציר#
try BLOCK catch (VAR) BLOCK
try BLOCK catch (VAR) BLOCK finally BLOCK
מה מקבלים בחזרה#
catch אינו קריאת פונקציה ואין לו ערך החזרה במובן של ביטוי. כמו מבני זרימת־בקרה אחרים, ערכו של המבנה try/catch כולו הוא ערך הבלוק שרץ אחרון - שימושי בתוך בלוק do או כביטוי הזנב של תת־שגרה:
my $value = do {
try {
fetch_thing(@args);
}
catch ($e) {
warn "fetch failed: $e";
$DEFAULT;
}
};
אם try רץ עד תום, המבנה מניב את הערך האחרון של בלוק try ו־VAR לעולם אינו נקשר. אם try מת, המבנה מניב את הערך האחרון של בלוק catch.
הצהרת ה־VAR#
The parentheses after catch are mandatory and must contain exactly one scalar variable declaration. It behaves like a my declaration
the
mykeyword is implicit, as in subroutine signatures:
catch ($e) { ... } # $e is a new lexical
catch (my $e) { ... } # syntax error - 'my' is already implied
ל־VAR יש תיחום לבלוק catch בלבד. הוא אינו נראה בבלוק try הקודם, בכל בלוק finally הבא אחריו, או אחרי המבנה. ערכו הוא מה שבלוק try זרק - מחרוזת, רשימה שעברה המרה למחרוזת לכזו, או הפניה, בדרך כלל עצם חריגה blessed. ראו die לאופן שבו ערך החריגה מיוצר.
בניגוד ל־eval, catch אינו קורא או מנקה את $@; החריגה נוסעת דרך VAR ישירות. $@ אינו נוגע על־ידי המבנה.
מצב גלובלי שהוא נוגע בו#
catch עצמו אינו נוגע במשתנים מיוחדים. מבנה try/catch בכללותו משאיר את $@ ללא שינוי - סטייה מכוונת מ־eval שמטרתה למנוע מקוראים לערבב את שני המטבעות. אם נדרשת בחינה בסגנון $@, יש להשתמש ב־eval במקום.
דוגמאות#
תפיסת חריגת מחרוזת פשוטה ודיווח עליה:
use feature 'try';
try {
risky();
}
catch ($e) {
warn "risky failed: $e";
}
בחינה של עצם חריגה מובנה:
try {
process($record);
}
catch ($e) {
if (ref($e) && $e->isa('My::NotFound')) {
return; # missing is fine
}
die $e; # re-throw everything else
}
זריקה מחדש ללא שינוי עם die. מכיוון ש־catch אינו נוגע ב־$@, זורקים מחדש את המשתנה שקושר, לא את $@:
try {
step();
}
catch ($e) {
log_error($e);
die $e; # propagates to the next handler
}
ייצור ערך מהמבנה ללא return. שימוש ב־return בתוך try או catch מחזיר מתת־השגרה העוטפת, לא מהמבנה - ראו מקרי קצה:
sub safe_parse {
my ($text) = @_;
try {
parse($text);
}
catch ($e) {
warn "parse error: $e";
undef; # value of the catch block
}
}
בלוק catch שעצמו מת - החריגה החדשה מופצת החוצה למטפל העוטף הבא (try/catch, eval, או יציאת תוכנית):
try {
open_db();
}
catch ($e) {
cleanup();
die "open_db failed: $e"; # thrown from inside catch
}
מקרי קצה#
אינו יכול לעמוד לבדו.
catch (VAR) { ... }ללא בלוקtryקודם הוא שגיאת תחביר.catchמנותח כחלק ממבנהtry, לא כמילת מפתח עצמאית.בדיוק סקלר אחד בסוגריים. הסוגריים הם חובה, חייבים להצהיר על סקלר אחד בדיוק, ומילת המפתח
myמשתמעת.catch { ... }ללא סוגריים הוא שגיאת תחביר.ל־
VARיש תיחום־בלוק. הוא קיים רק בתוך בלוקcatch. בלוקfinallyשבא אחריו אינו רואה אותו.חריגות שקריות עדיין מפעילות את המטפל.
tryמבדיל בין ״מת״ לבין ״הסתיים כרגיל״ על־ידי דגל שנקבע בזמן unwind, לא על־ידי אמיתיוּת של ערך החריגה.die 0,die "", ו־die undefכולם נכנסים לבלוקcatch;VARמקבל את הערך כפי שנזרק (עם ההמרה הרגילה שלdieשלundefל־"Died at ...").$@אינו נקבע. קוד שמושיט יד אל$@בתוך בלוקcatchקורא את$@של הקורא, לא את החריגה שזה עתה נתפסה. יש להשתמש ב־VAR.בלוק
catchעשוי למות. חריגות שמועלות מתוךcatchאינן נתפסות מחדש על־ידי אותו מבנה. הן מופצות למטפל העוטף הבא. זהו המנגנון לזריקה מחדש (die $e) ולדיווח על כשלי ניקוי מתוך המטפל עצמו.returnבתוךcatchמחזיר מתת־השגרה העוטפת, לא מבלוקcatch. הדבר שונה מ־evalBLOCK, שבוreturnהיה יוצא רק מה־eval. כדי להניב ערך מהמבנה, יש להשתמש בביטוי הסופי של הבלוק במקוםreturn.פקדי לולאה (
last,next,redo) בתוךcatchמכוונים ללולאה עוטפת, לא למבנה. מבנהtry/catchאינו עצמו לולאה.caller()אינו רואה את מסגרת ה־catch. מכיוון ש־catchאינו מיירט אתreturn, הוא בלתי נראה ל־caller, כמוwhileאוforeach.משויך רק ל־
tryשקדם לו מיד. לא ניתן לקבל שני בלוקיcatch, ולא למקם הוראות ביןtryל־catch. בלוקfinallyהאופציונלי, אם קיים, חייב לבוא אחריcatch.
הבדלים מ־upstream#
תואם מלא ל־upstream Perl 5.42.
ראו גם#
try- הבלוק שאת כשלו ה־catchהזה מטפל; תמיד משויך ל־catchאחד בדיוקfinally- בלוק שלישי אופציונלי שרץ ללא תלות בשאלה האם בלוקtryמת; אינו יכולreturnאו להשתמש בפקדי לולאהdie- מייצר את ערך החריגה ש־VARמקבל; יש להשתמש בו בתוךcatchכדי לזרוק מחדשeval- מטבע ישן יותר לטיפול בחריגות שמשתמש ב־$@; יש להושיט יד ל־try/catchבמקום בקוד חדש לתיחום ברור יותר ובלי foot-guns של$@