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:
catchvefinallybloklarıtrybloğ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ğeryalnızcatrybloğ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
finallybloğ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.