2014-04-13 18 views
13

Tôi đang cố triển khai phương pháp thay đổi giá trị của các trường trong đối tượng có thể có cấu trúc tùy ý. Sự tra tấn của các trường là không có vấn đề gì khi tôi có con trỏ tới một cấu trúc. Nhưng tôi không thể quản lý để thay đổi các lĩnh vực khi tôi có một giao diện mà không quấn một con trỏ đến một cấu trúc nhưng struct chính nó, trong ngắn hạn:Phản chiếu Golang: Không thể đặt trường giao diện bao quanh cấu trúc

// The following doesn't work 
var x interface{} = A{Str: "Hello"} 
// This panics: reflect: call of reflect.Value.Field on ptr Value 
reflect.ValueOf(&x).Field(0).SetString("Bye") 
// This panics: reflect: call of reflect.Value.Field on interface Value 
reflect.ValueOf(&x).Elem().Field(0).SetString("Bye") 
// This panics: reflect: reflect.Value.SetString using unaddressable value 
reflect.ValueOf(&x).Elem().Elem().Field(0).SetString("Bye") 
// This prints `false`. But I want this to be settable 
fmt.Println(reflect.ValueOf(&x).Elem().Elem().Field(0).CanSet()) 

// This works 
var z interface{} = &A{Str: "Hello"} 
// This prints `true` 
fmt.Println(reflect.ValueOf(z).Elem().Field(0).CanSet()) 

Trong dài: http://play.golang.org/p/OsnCPvOx8F

Tôi đã đọc The Laws of Reflection vì vậy tôi biết rằng tôi chỉ có thể sửa đổi các trường khi tôi có một con trỏ đến một cấu trúc. Vì vậy, câu hỏi của tôi bây giờ là: Làm cách nào để lấy con trỏ đến dữ liệu của cấu trúc?

UPDATE:

tôi nhận nó làm việc sử dụng cơ bản y := reflect.New(reflect.TypeOf(x)) nên các giá trị của y là settable bây giờ. Để biết ví dụ mở rộng, hãy xem điều này: https://gist.github.com/hvoecking/10772475

Trả lời

8

Có vẻ như bạn đang cố sửa đổi giá trị động được lưu trữ bên trong biến giao diện. Các hoạt động duy nhất bạn có thể thực hiện trên một biến giao diện là nhận hoặc thiết lập giá trị động (các hoạt động tạo bản sao) và để kiểm tra loại giá trị được lưu trữ.

Để hiểu tại sao mọi thứ theo cách này, hãy tưởng tượng rằng có như vậy một hoạt động và chúng tôi đã có đoạn mã sau:

var ptr *A = pointer_to_dynamic_value(x) 
x = B{...} 

ptr nay đại diện? Ngôn ngữ là miễn phí để sử dụng lại dung lượng lưu trữ khi gán giá trị mới cho biến giao diện, do đó, ptr hiện có thể trỏ đến bộ nhớ cho giá trị B, phá vỡ an toàn loại của ngôn ngữ (với lưu trữ trình biên dịch hiện tại chỉ được đảm bảo là tái sử dụng cho các giá trị nhỏ, nhưng điểm vẫn còn).

Cách an toàn duy nhất để thay đổi giá trị được lưu trữ trong giao diện là sao chép giá trị ra, sau đó gán lại phiên bản đã sửa đổi. Ví dụ:

a := x.(A) 
a.Str = "Bye" 
x = a 

Gói reflect phản ánh những hạn chế, do đó reflect.Value đại diện cho lĩnh vực giá trị động được coi là chỉ đọc.

Bạn có thể thiết lập các lĩnh vực trong ví dụ đầu tiên của bạn bởi vì giá trị động cho z là một con trỏ *A chứ không phải là struct bản thân: điều này có nghĩa các cấu trúc tham chiếu có thể được sửa đổi.

+1

Ah, tôi hiểu rồi. Cách của tôi xung quanh vấn đề đó sẽ là (để hỗ trợ các cấu trúc tùy ý) để tạo một cá thể mới với sự trợ giúp của 'y: = reflect.New (reflect.TypeOf (x))' và sao chép tất cả các trường của 'x' vào các trường tương ứng của 'y'. – Heye

+0

Cảm ơn Heye, bạn đã lưu tôi một giờ tìm kiếm khác. :) – Danyel

+0

Điều đó có ý nghĩa hoàn hảo; nhưng lý do tại sao bạn không thể thay đổi một trường bên trong một giá trị động, ví dụ "x. (danh sách) .head = z" - vì điều này không tạo ra một con trỏ lơ lửng. – WPWoodJr

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