scope
Kesinlikle işletilmeleri gereken ifadelerin finally
bloklarına, hatalı durumlarda işletilmeleri gereken ifadelerin de catch
bloklarına yazıldıklarını bir önceki bölümde gördük. Bu blokların kullanımlarıyla ilgili bir kaç gözlemde bulunabiliriz:
catch
vefinally
bloklarıtry
bloğu olmadan kullanılamaz.- Bu bloklarda kullanılmak istenen bazı değişkenler o noktalarda geçerli olmayabilirler:
void birİşlev(ref int çıkış) { try { int birDeğer = 42; çıkış += birDeğer; hataAtabilecekBirİşlev(); } catch (Exception hata) { çıkış -= birDeğer; // ← derleme HATASI } }
Yukarıdaki işlev, referans türündeki parametresinde değişiklik yapmakta ve hata atılması durumunda onu eski haline getirmeye çalışmaktadır. Ne yazık ki,
birDeğer
yalnızcatry
bloğu içinde tanımlı olduğu için bir derleme hatası alınır. (Not: Yaşam süreçleriyle ilgili olan bu konuyu ilerideki bir bölümde tekrar değineceğim.) - Bir kapsamdan çıkılırken kesinlikle işletilmesi gereken ifadelerin hepsinin bir arada en aşağıdaki
finally
bloğuna yazılmaları, ilgili oldukları kodlardan uzakta kalacakları için istenmeyebilir.
catch
ve finally
bloklarına benzer şekilde işleyen ve bazı durumlarda daha uygun olan olanak scope
deyimidir. Üç farklı scope
kullanımı, yine ifadelerin kapsamlardan çıkılırken kesinlikle işletilmeleri ile ilgilidir:
scope(success)
: Kapsamdan başarıyla çıkılırken işletilecek olan ifadeleri belirler.scope(failure)
: Kapsamdan hatayla çıkılırken işletilecek olan ifadeleri belirler.scope(exit)
: Kapsamdan başarıyla veya hatayla çıkılırken işletilecek olan ifadeleri belirler.
Bu deyimler yine atılan hatalarla ilgili olsalar da try-catch
bloklarının parçası değillerdir.
Örneğin, hata atıldığında çıkış
'ın değerini düzeltmeye çalışan yukarıdaki işlevi bir scope(failure)
deyimiyle daha kısa olarak şöyle yazabiliriz:
void birİşlev(ref int çıkış) { int birDeğer = 42; çıkış += birDeğer; scope(failure) çıkış -= birDeğer; hataAtabilecekBirİşlev(); }
Yukarıdaki scope
deyimi, kendisinden sonra yazılan ifadenin işlevden hata ile çıkıldığı durumda işletileceğini bildirir. Bunun bir yararı, yapılan bir değişikliğin hatalı durumda geri çevrilecek olduğunun tam da değişikliğin yapıldığı yerde görülebilmesidir.
scope
deyimleri bloklar halinde de bildirilebilirler:
scope(exit) { // ... çıkarken işletilecek olan ifadeler ... }
Bu kavramları deneyen bir işlevi şöyle yazabiliriz:
void deneme() { scope(exit) writeln("çıkarken 1"); scope(success) { writeln("başarılıysa 1"); writeln("başarılıysa 2"); } scope(failure) writeln("hata atılırsa 1"); scope(exit) writeln("çıkarken 2"); scope(failure) writeln("hata atılırsa 2"); yüzdeElliHataAtanİşlev(); }
İşlevin çıktısı, hata atılmayan durumda yalnızca scope(exit)
ve scope(success)
ifadelerini içerir:
çıkarken 2 başarılıysa 1 başarılıysa 2 çıkarken 1
Hata atılan durumda ise scope(exit)
ve scope(failure)
ifadelerini içerir:
hata atılırsa 2 çıkarken 2 hata atılırsa 1 çıkarken 1 object.Exception: hata mesajı
Çıktılardan anlaşıldığı gibi, scope
deyimlerinin ifadeleri ters sırada işletilmektedir. Bunun nedeni, daha sonra gelen kodların daha önceki değişkenlerin durumlarına bağlı olabilecekleridir. scope
deyimlerindeki ifadelerinin ters sırada işletilmeleri programın durumunda yapılan değişikliklerin geri adımlar atılarak ters sırada işletilmelerini sağlar.