ProgramlamaBackend Geliştirici

Go'daki işlevler ve işlevlerle birlikte defer kullanmanın özellikleri ve tuzakları nelerdir?

Hintsage yapay zeka asistanı ile mülakatları geçin

Cevap.

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.

Meselenin Tarihçesi

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.

Sorun

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.

Çözüm

İş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:

  • defer her zaman argümanların ve resiverların, beyanda bulunduğu andaki kopyasını alır.
  • Değer resiverli işlevler defer ile dış nesneyi etkilemez.
  • İşaretçi resiverli işlevler defer ile orijinali değiştirir.

Soru-Cevaplar.

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 }

Tipik hatalar ve anti-patentler

  • Değer resiver ile işlev içinde defer ile kalıcı bir nesneyi değiştirmeye çalışmak.
  • Defer kullanırken yapıların kopyalanmasını göz ardı etmek.

Gerçek Hayattan Bir Örnek

Olumsuz Durum

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ı:

  • Kod sade ve okunulabilir.

Eksileri:

  • Temizlik gerçekleşmeyince bağlantılar sızdı.

Olumlu Durum

Orijinal nesneyi değiştirerek sonlandırmalar için işaretçi resiverli işlevler kullandık.

Artıları:

  • Durum düzgün bir şekilde değişiyor, kaynaklar temizleniyor.

Eksileri:

  • Hangi durumlarda işaretçi, hangi durumlarda değer kullanılması gerektiğine dikkat edilmesi gerekiyor.