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εκτελείται, και μετά τίθεται σε ισχύ η επιστροφή.Έλεγχος βρόχου (
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 εκεί όπου η μεταβλητή είναι εν ζωή.
Παραδείγματα#
Πάντα κλείστε ένα handle, ακόμη και σε σφάλμα:
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, που συνήθως παράγει ένα μπερδεμένο, άσχετο σφάλμα. Ενεργοποιείτε πάντα το χαρακτηριστικό πριν χρησιμοποιήσετε τη σύνταξη.
Διαφορές από το upstream#
Πλήρως συμβατό με το upstream 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 ($@)με καθαρότερη εμβέλεια και σωστή σημασιολογία ροής ελέγχου