2012-04-12 17 views
5

Mam problem z przeniesieniem zmiennej do anonimowej funkcji. Czy istnieje rozwiązanie?Go: transfer var do anonimowej funkcji

import "github.com/lxn/walk" 

*** 

var openAction [12]*walk.Action 
for i := 0; i < 12; i++ { 

    openBmp, err := walk.NewBitmapFromFile(_films[i][0]) 
    if err != nil { 
     log.Printf("Open bitmap for buildBody() :%v\n", err) 
    } 
    openAction[i] = walk.NewAction() 
    openAction[i].SetImage(openBmp) 
    openAction[i].SetText(_films[i][2]) 
    openAction[i].Triggered().Attach(func(){ 
     exec(i) 
    }) 
    mw.ToolBar().Actions().Add(openAction[i]) 
} 

exec (i) gdzie zawsze = 11

+0

Otrzymałem również odpowiedzi w [Najczęściej zadawanych pytaniach] (http://golang.org/doc/go_faq.html#closures_and_goroutines). – kostix

Odpowiedz

10
for i := 0; i < 12; i++ { 
    i := i 
    ... 

Crazy, wygląda, to jest coś, co zobaczysz w kodzie Go. Wynika to ze sposobu działania zamknięć i sposobu, w jaki zmienne mają zasięg. Twoja anonimowa funkcja to zamknięcie przechwytujące i. W szczególności przechwytuje zmienną o nazwie i, a nie aktualną wartość i, i przechwytuje to, co ja w zakresie. W twoim oryginalnym kodzie jest to zmienna pętli, która jest tą samą zmienną dla każdej iteracji pętli. Wszystkie twoje zamknięcia przechwyciły tę samą zmienną. Dodanie i := i deklaruje nową zmienną w każdej iteracji. Teraz każde zamknięcie przechwyci tę nową zmienną, a przy każdej iteracji będzie to inna zmienna.

W nieco bardziej szczegółowym zakresie zmienna pętli i jest instrukcją for. Obejmuje to blok pętli, ale ponieważ deklaracja zmiennej pętli i znajduje się poza blokiem, deklarowanie nowej zmiennej o tej samej nazwie wewnątrz bloku jest legalne i tworzy nową zmienną w tym punkcie bloku. Zmienna pętli jest następnie zacieniona. Często deklarowana taka zmienna idzie na stos, ale w tym przypadku analiza ucieczki kompilatora pokazuje, że twoje zamknięcie wciąż odnosi się do tej zmiennej blokowej, gdy wykracza ona poza zakres na końcu bloku, a więc zmienna jest umieszczona na sterta. Przy każdej iteracji blok jest ponownie wprowadzany i nowa zmienna i jest umieszczana na stercie.

+0

Wyglądaj łatwo i czego potrzebuję. – Vladislav

+0

To naprawdę fajny hack. Będę musiał pamiętać tę sztuczkę. –

6

myślę, że to będzie Ci to, co chcesz:

openAction[i].Triggered().Attach(func(x int) func() { 
    return func() { exec(x) } 
}(i)) 

Sztuką jest mieć swój anonimowa funkcja zwracać funkcja anonimowa, a każda utworzona funkcja będzie obejmowała każdą z wartości: i.

4

Występuje dziwactwo "go do pętli". Zmienna i w pętli nie jest nową zmienną dla każdej iteracji. z tego powodu wszystkie twoje zamknięcia zamykają się nad tą samą zmienną, której wartość zmienia się pod nimi. Kiedy twój kod będzie działał po pętli, wszystkie funkcje zobaczą wartość 11 dla i, które zamknęli.

Rozwiązaniem jest przekazanie funkcji i, która następnie zwraca inną funkcję, która zamyka funkcje arg. Dlatego działa rozwiązanie Adama Crosslandsa.

Powiązane problemy