Go'da defer anahtar kelimesi, bir işlevin tamamlanmasının ardından bir başka işlevin çalıştırılmasını ertelemektedir. Bu, kaynakların serbest bırakılması ve sonlandırma işlemleri için kullanışlıdır. Ancak defer'in, işaretçi (*T) veya değer (T) alan işlevlerle birlikte kullanımı, belli belirsiz detaylar taşımaktadır.
Go, kod güvenliği sebepleriyle, işlevden çıkış noktasına bakılmaksızın otomatik olarak temizlik kodunun çağrılmasını sağlamak üzere tasarlanmıştır. Ancak kullanılan resiver türü (işaretçi veya değer) defer'in işlevi çağıran işlevin nesnesinin kopyalanıp kopyalanmayacağını veya orijinal nesne ile değişip değişmeyeceğini belirlemektedir.
Eğer bir işlev, değer resiver ile çağrılıyorsa (T), defer içinde yapı değeri kopyalanmaktadır. Eğer bir işlev, işaretçi resiver ile çağrılıyorsa (*T), işlev orijinal nesne ile işlem yapmaktadır. Sonuç olarak, defer işlevinde değerler değiştirilirse, bu değişiklikler görünmeyecek; işaretçi ile yapılan değişiklikler ise dış yapıda gösterilecektir. Bu, özellikle defer üzerinden bir nesnenin durumunu değiştirmeye çalışırken zor tespit edilen hatalara yol açar.
İşlev tasarlarken ve defer kullanırken, resiver türünü her zaman bilinçli bir şekilde seçmek önemlidir. İşlevin sonuna kadar dayanması gereken değişiklikler, işaretçi aracılığıyla gerçekleştirilmelidir.
Örnek kod:
package main import "fmt" type Counter struct { Value int } func (c Counter) IncValue() { // değer resiver ile işlev defer func() { c.Value++ // yalnızca kopya artırılır fmt.Println("[Değer alıcısı defer] Değer:", c.Value) }() } func (c *Counter) IncPointer() { // işaretçi resiver ile işlev defer func() { c.Value++ // orijinal artırılır fmt.Println("[İşaretçi alıcısı defer] Değer:", c.Value) }() } func main() { c := Counter{Value: 10} c.IncValue() // Değer 10 kalır c.IncPointer() // Değer 11 olur fmt.Println("Çağrılardan sonraki Orijinal Değer:", c.Value) }
Önemli özellikler:
1. Eğer defer içinde, değer resiver alan bir işlev çağrılırsa, dış nesne değişir mi?
Hayır, defer içinde orijinal nesne değişmeyecek çünkü işlev bir yapı kopyası ile çalışmaktadır.
2. Değer ile işlev çağırarak sağlanan yapı durumunu garanti altına almak için defer'e güvenilir mi?
Hayır. Orijinal nesnede değişiklik yansıtmak istiyorsanız, işaretçi ile yönlendirilmiş işlevleri kullanmalısınız.
3. Defer'den sonra yapı alanlarını değiştirirsek ne olur?
Eylemler, resiver türüne bağlı olarak değişir: Eğer metod/fonksiyon bir kopya alıyorsa, defer bildirildiği andan sonra yapılan değişiklikler, defer işlevinde görünmeyecektir.
func (c Counter) Demo() { defer fmt.Println("defer c.Value:", c.Value) // güncel değer alacak c.Value = 42 // defer'i etkilemeyecek }
Bir bağlantıyı kapatma işlevi yazarken, durumunu değer ile işlev üzerinden güncelleyerek defer kullandık. Kapatma bayrağının güncellenmediğini fark ettik.
Artıları:
Eksileri:
Orijinal nesneyi değiştirerek sonlandırmalar için işaretçi resiverli işlevler kullandık.
Artıları:
Eksileri: