2012-04-12 26 views
5

Tôi đang gặp sự cố khi chuyển một biến thành một hàm ẩn danh. Có giải pháp nào không?Go: chuyển var vào chức năng ẩn danh

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) nơi mà tôi luôn = 11

+0

Được trả lời trong [FAQ] (http://golang.org/doc/go_faq.html#closures_and_goroutines). – kostix

Trả lời

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

Thật điên rồ, đây là thứ bạn sẽ thấy trong mã Go. Nó kết quả từ cách đóng cửa làm việc và cách biến được phạm vi. Chức năng ẩn danh của bạn là một đóng cửa bắt giữ tôi. Cụ thể, nó đang nắm bắt một biến có tên là i, không phải là giá trị hiện tại của i, và nó nắm bắt bất kỳ thứ gì tôi đang ở trong phạm vi. Trong mã ban đầu của bạn, đây là biến vòng lặp, là biến tương tự cho mỗi lần lặp của vòng lặp. Tất cả các đóng của bạn đã nắm bắt cùng một biến. Việc thêm i := i tuyên bố biến mới trên mỗi lần lặp. Bây giờ mỗi đóng sẽ nắm bắt biến mới này, và trên mỗi lần lặp nó sẽ là một biến khác.

Chi tiết hơn một chút, phạm vi của biến vòng lặp i là câu lệnh for. Điều này bao gồm khối vòng lặp, nhưng kể từ khi khai báo biến vòng lặp i nằm ngoài khối, khai báo một biến mới có cùng tên trong khối là hợp pháp và tạo một biến mới tại điểm đó trong khối. Biến vòng lặp sau đó sẽ bị che khuất. Thường thì một biến được khai báo như thế này sẽ nằm trong ngăn xếp, nhưng trong trường hợp này phân tích thoát trình biên dịch thấy rằng việc đóng của bạn vẫn đề cập đến biến khối này khi nó nằm ngoài phạm vi của khối, và do đó biến được đặt trên đống. Trên mỗi lần lặp lại, khối được nhập lại và một biến mới i được đặt trên heap.

+0

Nhìn dễ dàng và những gì tôi cần. – Vladislav

+0

Thats thực sự là một hack gọn gàng. Tôi sẽ phải nhớ điều đó. –

6

Tôi nghĩ rằng điều này sẽ giúp bạn có được những gì bạn muốn:

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

Bí quyết là phải có chức năng ẩn danh của bạn trả về một chức năng ẩn danh và mỗi hàm được tạo sẽ bao gồm mỗi giá trị của i.

4

Bạn đang gặp phải sự lừa đảo của go cho vòng lặp. Biến i trong vòng lặp không phải là biến mới cho mỗi lần lặp. bởi vì tất cả các đóng của bạn đang đóng trên cùng một biến có giá trị đang thay đổi bên dưới chúng. Khi mã của bạn chạy sau vòng lặp, tất cả các hàm sẽ thấy giá trị 11 cho i mà chúng đóng lại.

Giải pháp là chuyển i vào hàm mà sau đó trả về một hàm khác đóng trên các hàm arg. Đây là lý do giải pháp Adam Crosslands hoạt động.

Các vấn đề liên quan