5

変数を無名関数に転送するのに問題があります。解決策はありますか?

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)ここで、iは常に= 11

4

3 に答える 3

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

見た目はクレイジーですが、これはGoコードに表示されるものです。これは、クロージャが機能する方法と変数のスコープが設定される方法に起因します。あなたの無名関数はiをキャプチャするクロージャです。具体的には、iの現在の値ではなく、iと呼ばれる変数をキャプチャし、スコープ内にあるすべてのiをキャプチャします。元のコードでは、これはループ変数であり、ループの各反復で同じ変数です。すべてのクロージャが同じ変数をキャプチャしました。を追加するとi := i、反復ごとに新しい変数が宣言されます。これで、各クロージャはこの新しい変数をキャプチャし、反復ごとに異なる変数になります。

もう少し詳しく説明すると、ループ変数iのスコープはforステートメントです。これにはループブロックが含まれますが、ループ変数iの宣言はブロックの外側にあるため、ブロック内で同じ名前の新しい変数を宣言することは有効であり、ブロック内のその時点で新しい変数を作成します。次に、ループ変数がシャドウされます。多くの場合、このように宣言された変数はスタックに配置されますが、この場合、コンパイラエスケープ分析では、ブロックの最後でスコープ外になったときに、クロージャがこのブロック変数を参照していることがわかり、変数はヒープ。各反復で、ブロックが再入力され、新しい変数iがヒープに配置されます。

于 2012-04-12T03:41:42.260 に答える
6

私はこれがあなたが望むものを手に入れると思います:

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

秘訣は、無名関数に無名関数を返すようにすることです。作成された各関数は、iの各値を囲みます。

于 2012-04-12T02:31:23.980 に答える
4

あなたはgoのforループの癖に遭遇しています。ループ内のi変数は、各反復の新しい変数ではありません。このため、すべてのクロージャは、その下で値が変化している同じ変数を閉じています。コードがループの後に実行されると、すべての関数は、閉じたiの値11を確認します。

解決策は、iを関数に渡し、関数argを閉じる別の関数を返すことです。これが、AdamCrosslandsソリューションが機能する理由です。

于 2012-04-12T02:41:51.090 に答える