Sử dụng phản xạ, làm cách nào để bạn đặt giá trị của trường cấu trúc?


106

gặp khó khăn khi làm việc với các trường struct bằng reflectgói. cụ thể là chưa tìm ra cách đặt giá trị trường.

kiểu t struct {fi int; chuỗi fs}
var rt = t {123, "jblow"}
var i64 int64 = 456
  1. lấy Tên của trường tôi - điều này có vẻ hoạt động

    var field = reflect.TypeOf(r).Field(i).Name

  2. nhận giá trị của trường i dưới dạng a) interface {}, b) int - điều này có vẻ hoạt động

    var iface interface{} = reflect.ValueOf(r).Field(i).Interface()

    var i int = int(reflect.ValueOf(r).Field(i).Int())

  3. giá trị cài đặt của trường i - thử một - hoảng sợ

    reflect.ValueOf(r).Field(i).SetInt( i64 )

    hoảng sợ : phản ánh.Value · SetInt sử dụng giá trị thu được bằng cách sử dụng trường chưa được báo cáo

    giả sử nó không thích các tên trường "id" và "name", vì vậy được đổi tên thành "Id" và "Name"

    a) giả thiết này có đúng không?

    b) nếu đúng, nghĩ là không cần thiết vì trong cùng một tệp / gói

  4. cài đặt giá trị của trường i - thử hai (với tên trường được viết hoa) - hoảng sợ

    reflect.ValueOf(r).Field(i).SetInt( 465 )

    reflect.ValueOf(r).Field(i).SetInt( i64 )

    hoảng sợ : phản ánh.Value · SetInt sử dụng giá trị không thể giải nén


Hướng dẫn dưới đây của @peterSO là kỹ lưỡng và chất lượng cao

Bốn. những công việc này:

reflect.ValueOf(&r).Elem().Field(i).SetInt( i64 )

tài liệu của anh ấy cũng như các tên trường phải có thể xuất được (bắt đầu bằng chữ in hoa)


ví dụ gần nhất mà tôi có thể tìm thấy cho một người nào đó sử dụng reflectđể thiết lập dữ liệu là comments.gmane.org/gmane.comp.lang.go.general/35045 , nhưng thậm chí ở đó anh ta đã từng json.Unmarshallàm công việc bẩn thỉu thực sự
cc young

(nhận xét ở trên đã lỗi thời)
cc young

Câu trả lời:


156

Go có sẵn dưới dạng mã nguồn mở . Một cách tốt để tìm hiểu về phản xạ là xem cách các nhà phát triển cờ vây cốt lõi sử dụng nó. Ví dụ, gói Go fmtjson . Tài liệu gói có liên kết đến tệp mã nguồn dưới tiêu đề Tệp gói.

Gói Go json điều phối và giải nén JSON từ và tới cấu trúc Go.


Đây là một ví dụ từng bước đặt giá trị của một structtrường trong khi cẩn thận tránh các lỗi.

Cờ vây reflect gói có một CanAddrchức năng.

func (v Value) CanAddr() bool

CanAddr trả về true nếu địa chỉ của giá trị có thể được lấy bằng Addr. Các giá trị như vậy được gọi là địa chỉ. Một giá trị có thể định địa chỉ được nếu nó là một phần tử của một lát cắt, một phần tử của một mảng có thể định địa chỉ, một trường của một cấu trúc có thể định địa chỉ hoặc kết quả của việc tham chiếu một con trỏ. Nếu CanAddr trả về false, việc gọi Addr sẽ hoảng loạn.

reflectGói Go có một CanSetchức năng, nếu true, ngụ ý rằng đó CanAddrcũng làtrue .

func (v Value) CanSet() bool

CanSet trả về true nếu giá trị của v có thể thay đổi. Giá trị chỉ có thể được thay đổi nếu nó có thể được định địa chỉ và không có được bằng cách sử dụng các trường cấu trúc chưa được báo cáo. Nếu CanSet trả về false, việc gọi Set hoặc bất kỳ setter kiểu cụ thể nào (ví dụ: SetBool, SetInt64) sẽ hoảng sợ.

Chúng ta cần phải chắc chắn rằng chúng ta có thể Setsựstruct sân. Ví dụ,

package main

import (
    "fmt"
    "reflect"
)

func main() {
    type t struct {
        N int
    }
    var n = t{42}
    // N at start
    fmt.Println(n.N)
    // pointer to struct - addressable
    ps := reflect.ValueOf(&n)
    // struct
    s := ps.Elem()
    if s.Kind() == reflect.Struct {
        // exported field
        f := s.FieldByName("N")
        if f.IsValid() {
            // A Value can be changed only if it is 
            // addressable and was not obtained by 
            // the use of unexported struct fields.
            if f.CanSet() {
                // change value of N
                if f.Kind() == reflect.Int {
                    x := int64(7)
                    if !f.OverflowInt(x) {
                        f.SetInt(x)
                    }
                }
            }
        }
    }
    // N at end
    fmt.Println(n.N)
}

Output:
42
7

Nếu chúng ta có thể chắc chắn rằng tất cả các kiểm tra lỗi là không cần thiết, ví dụ này sẽ đơn giản hóa thành,

package main

import (
    "fmt"
    "reflect"
)

func main() {
    type t struct {
        N int
    }
    var n = t{42}
    fmt.Println(n.N)
    reflect.ValueOf(&n).Elem().FieldByName("N").SetInt(7)
    fmt.Println(n.N)
}

5
Bỏ cuộc! ở đâu đó có một câu trả lời, nhưng bốn giờ làm việc trên pkg json đã không mang lại cho tôi. lại pkg phản ánh, việc kéo thông tin khá đơn giản, nhưng việc thiết lập dữ liệu đòi hỏi một số phép thuật đen mà tôi rất muốn xem một ví dụ đơn giản ở đâu đó!
cc young

2
Nổi bật! Nếu bạn đã từng ở Thái Lan, hãy để tôi chiêu đãi bạn một hoặc hai hoặc ba ly bia! cảm ơn bạn rất nhiều
cc young

5
Ví dụ thực tế tuyệt vời, bài viết này đã giải mã hoàn toàn cho tôi golang.org/doc/articles/laws_of_reflection.html
danmux

Tuyệt vời example.Here là mẫu sân chơi của cùng mã play.golang.org/p/RK8jR_9rPh
Sarath Sadasivan Pillai

Điều này không đặt trường cấu trúc. Điều này đặt một trường trong một con trỏ đến cấu trúc. Rõ ràng, đây không phải là câu trả lời cho câu hỏi mà OP đặt ra.
wvxvw

13

Điều này dường như hoạt động:

package main

import (
    "fmt"
    "reflect"
)

type Foo struct {
    Number int
    Text string
}

func main() {
    foo := Foo{123, "Hello"}

    fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))

    reflect.ValueOf(&foo).Elem().Field(0).SetInt(321)

    fmt.Println(int(reflect.ValueOf(foo).Field(0).Int()))
}

Bản in:

123
321

cảm ơn! bây giờ tôi đã đọc ghi chú của peterSO, điều này hoàn toàn hợp lý. Tôi đang sử dụng foo, không phải & foo, vì vậy không thể thay đổi được và không chắc về nội dung của Elem ().
cc young
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.