Go 'defer' ~ Al

ALcatraz'

Kıdemli Üye
30 May 2013
4,338
4
İstanbul
Go, diğer programlama dillerinde yaygın olarak kullanılan kontrol akışı keywordlerinin çoğunu içerir; ör. "if", "switch", "for" gibi. Diğer programlama dillerinde bulunmayan bir keyword "defer" dir. Daha az sıklıkta olsa bile, programlarınızda ne kadar yararlı olabileceğini hızlı bir şekilde göreceksiniz.

Bir "+defer" ifadesinin ana kullanımlarından biri, açık dosyalar, ağ bağlantıları ve database-handles gibi kaynakları temizlemektir. Programınız bu kaynaklarla tamamlandığında, program sınırlarını tüketmemek ve diğer programların bu kaynaklara erişmesine izin vermemek için bunları kapatmamız gerekir. `defer ', açık çağrının yakınında dosyayı / kaynağı kapatmak için çağrıları tutarak kodumuzu daha temiz ve hatalara daha az eğilimli yapar.

Bu makalede, kaynakları temizlemek için "defer" keyword'ünün nasıl kullanılacağını ve "defer" kullanılırken sık karşılaşılan bazı hataların nasıl çözüleceğini göreceğiz.Stack'e yapılan tüm çağrılar, eklendiği işlev döndüğünde çağrılır. Aramalar bir stacküzerinde depolandığından, son giriş ve ilk çıkış sırasıyla çağrılır.

En basit örneklerinden Println fonksiyonu ve "defer" işleminin nasıl çalıştığını görelim:

Kod:
package main
import "fmt"

func main() {
   defer fmt.Println("Valentina")
   fmt.Println("Retarded")
}

Main fonksiyonumuz'da defer keyword'ü ile başlayan Println fonksiyonumuz 'Valentina' çıktısını veriyor. Onu takip eden Println fonksiyonumuzda ise 'Retarded' çıktısını veriyor. Normalde ilk Println fonksiyonumuz Valentina'yı vermesi gerekirken çalıştırdığımızda şu şekilde bir çıktı görürüz;

Kod:
'Retarded
Valentina'

Şimdi de bir kaç yorum satırı ekleyip kodumuzu anlamlandırmaya çalışalım;

Kod:
package main

import "fmt"

func main() {
   // defer ifadesi yürütülüyor
   // fmt.Println("[COLOR="white"]Valentina[/COLOR]") fonksiyon return etmeden önce çağrılacak bi listede
   defer fmt.Println("[COLOR="white"]Valentina[/COLOR]")

   // Bu satır hemen işleve sokuluyor
   fmt.Println("Retarded")

   // fmt.Println*("[COLOR="white"]Valentina[/COLOR]") Fonksiyonun sonuna geldiğimiz için defer ile tanımladığımız işlev çağrılıyor
}

"defer" yi anlamanın kısa yolu, erteleme işlevi için bağımsız değişkenlerin "defer" deyimi yürütüldüğünde hemen değerlendirilmesidir. Bir defer `yürütüldüğünde, işlev geri gelmeden önce çağrılması gereken aşağıdaki ifadeyi bir listeye yerleştirir.

Bu kod "defer" nin yürütüldüğü sırayı temsil etse de, bu bir Go programı yazmak için tipik bir yol değildir. Dosya tanıtıcısı gibi bir kaynağı temizlemek için `defer 'kullanmamız daha olasıdır. Nasıl çalıştığını görelim.

Kod:
package main

import (
   "io"
   "log"
   "os"
)

func main() {
   if err := write("readme.txt", "This is a readme file"); err != nil {
       log.Fatal("failed to write file:", err)
   }
}

func write(fileName string, text string) error {
   file, err := os.Create(fileName)
   if err != nil {
       return err
   }
   _, err = io.WriteString(file, text)
   if err != nil {
       return err
   }
    file.Close()
   return nil
}

Bu programda, ilk olarak bir dosya oluşturmaya çalışan "write" adlı bir fonksiyon vardır. Bir hata varsa, hata döndürülür ve fonksiyon sonlandırılır. Ardından, belirtilen dosyaya "This is a readme file" text'i yazma girişiminde bulunuldu. Bir hata oluşursa, hata döndürülür ve fonksiyon sona erer. Fonksiyon daha sonra dosyayı kapatmaya ve kaynağı sisteme geri bırakmaya çalışır. Son olarak, fonksiyonun hatasız yürütüldüğünü belirtmek için "nil" fonksiyonu döndürür.

Bu kod çalışmasına rağmen,bir hata var. "io.WriteString" çağrısı başarısız olursa, fonksiyon dosyayı kapatmadan ve kaynağı sisteme döndürmeden geri döner.

Başka bir file.Close() ifadesi ekleyerek sorunu çözebiliriz. Yani bunu defer olmadan da çözersiniz:

Kod:
package main

import (
   "io"
   "log"
   "os"
)

func main() {
   if err := write("readme.txt", "This is a readme file"); err != nil {
       log.Fatal("failed to write file:", err)
   }
}

func write(fileName string, text string) error {
   file, err := os.Create(fileName)
   if err != nil {
       return err
   }
   _, err = io.WriteString(file, text)
   if err != nil {

       return err
   }
   [COLOR="yellowgreen"]file.Close()[/COLOR]
   return nil
}

"io.WriteString" çağrısı başarısız olsa bile dosya hala kapalıdır. Bu, daha karmaşık bir fonksiyon olan bir hata tespit etmek ve düzeltmek nispeten kolay olsa da, gözden kaçırılmış olabilir.

"file.Close()" öğesine ikinci çağrıyı eklemek yerine, yürütme sırasında hangi dalların kullanıldığından bağımsız olarak "Close()" öğesinin her zaman çağrıldığından emin olmak için bir "defer" ifadesi kullanabiliriz.

"defer" keyword'ünü kullanalım:

Kod:
package main

import (
    "io"
    "log"
    "os"
)

func main() {
    if err := write("readme.txt", "This is a readme file"); err != nil {
        log.Fatal("failed to write file:", err)
    }
}

func write(fileName string, text string) error {
    file, err := os.Create(fileName)
    if err != nil {
        return err
    }
    [COLOR="yellowgreen"]defer file.Close()[/COLOR]
    _, err = io.WriteString(file, text)
    if err != nil {
        return err
    }
    return nil
}

Bu sefer şu kod satırlarını ekledik:
Kod:
defer file.Close()
. Bu derleyiciye write fonksiyonundan çıkmadan önce file.Close() 'yı yürütmesini söyler. Gelecekte daha fazla kod eklesek ve fonksiyonu bırakan başka bir dal oluştursak bile dosyayı temizleyip kapatmamızı sağladık.

Ancak deferekleyerek başka bir hata daha ekledik. Artık "Close" yöntemiyle döndürülebilecek olası hatayı kontrol etmiyoruz. Bunun nedeni, "defer" kullanılırken fonksiyonumuza bir dönüş değeri döndürmenin bir yolu olmamasıdır.

Go, programınızın davranışını etkilemeden Close() öğesini birden çok kez çağırmanın güvenli ve kabul edilmiş bir yöntem olduğunu düşünür. Close() bir hata döndürürse, bu ilk çağrıldığında yapılır. Bu, onu başarılı yürütme yolunda açıkça işlevimize çağırmamızı sağlar.

"Close" çağrısını nasıl erteleyebileceğimize bakalım ve bir tanesiyle karşılaştığımızda bir hata raporlayalım:

Kod:
package main

import (
    "io"
    "log"
    "os"
)

func main() {
    if err := write("readme.txt", "This is a readme file"); err != nil {
        log.Fatal("failed to write file:", err)
    }
}

func write(fileName string, text string) error {
    file, err := os.Create(fileName)
    if err != nil {
        return err
    }
    defer file.Close()
    _, err = io.WriteString(file, text)
    if err != nil {
        return err
    }

    return [COLOR="YellowGreen"]file.Close()[/COLOR]
}

Bu programdaki tek değişiklik, file.Close() döndürdüğümüz son satırdır. Close çağrısı bir hataya yol açarsa, bu artık beklendiği gibi returnfonskiyonuna geri döner. defer file.Close() ifadesinin de return ifadesinden sonra yürütüldüğünü unutmayın. Bu, file.Close() öğesinin iki kez çağrılabileceği anlamına gelir. Bu ideal olmasa da, programınız üzerinde herhangi bir yan etkiye neden olmaması gerektiği için kabul edilebilir bir eylem şeklidir.

Ancak, fonksiyonda daha önce bir hata oluşursa, ör. "WriteString" çağrılırken, fonksiyon bu hatayı döndürür ve ertelendiği için "file.Close" işlevini çağırmaya çalışır. `File.Close'' bir hata döndürse de (ve muhtemelen de), artık ilgilenmiyoruz çünkü başlangıçta neyin yanlış gittiğini bize söyleyen bir hata aldık. Şimdiye kadar tek bir gecikmeyle kaynaklarımızı düzgün bir şekilde temizlememizi nasıl sağlayabileceğimizi gördük. Daha sonra, birden fazla kaynağı temizlemek için birden fazla defer ifadesini nasıl kullanabileceğimizi göreceğiz.

Bir fonksiyonun birden fazla "defer" ifadesi içermesi normaldir. Çoklu defer uyguladığımızda ne olacağını görmek için sadece "defer" talimatlarını içeren bir program oluşturalım:
Kod:
package main

import "fmt"

func main() {
   defer fmt.Println("[COLOR="white"]tina[/COLOR]")
   defer fmt.Println("[COLOR="white"]en[/COLOR]")
   defer fmt.Println("[COLOR="white"]Val[/COLOR]")
}

Sizce çıktısı ne olacak? Ufak bi' ipucu Last In First Out

Source
 
Üst

Turkhackteam.org internet sitesi 5651 sayılı kanun’un 2. maddesinin 1. fıkrasının m) bendi ile aynı kanunun 5. maddesi kapsamında "Yer Sağlayıcı" konumundadır. İçerikler ön onay olmaksızın tamamen kullanıcılar tarafından oluşturulmaktadır. Turkhackteam.org; Yer sağlayıcı olarak, kullanıcılar tarafından oluşturulan içeriği ya da hukuka aykırı paylaşımı kontrol etmekle ya da araştırmakla yükümlü değildir. Türkhackteam saldırı timleri Türk sitelerine hiçbir zararlı faaliyette bulunmaz. Türkhackteam üyelerinin yaptığı bireysel hack faaliyetlerinden Türkhackteam sorumlu değildir. Sitelerinize Türkhackteam ismi kullanılarak hack faaliyetinde bulunulursa, site-sunucu erişim loglarından bu faaliyeti gerçekleştiren ip adresini tespit edip diğer kanıtlarla birlikte savcılığa suç duyurusunda bulununuz.