reflect를 사용하여 struct 필드의 값을 어떻게 설정합니까?


106

reflect패키지를 사용하여 구조체 필드로 작업하는 데 어려움을 겪고 있습니다. 특히 필드 값을 설정하는 방법을 찾지 못했습니다.

유형 t struct {fi int; fs 문자열}
var rt = t {123, "jblow"}
var i64 int64 = 456
  1. 필드 i의 이름 가져 오기-작동하는 것 같습니다.

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

  2. 필드 i의 값을 a) 인터페이스 {}, b) int-작동하는 것 같습니다.

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

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

  3. 필드 i의 값 설정-시도해보기-패닉

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

    panic : 반출되지 않은 필드를 사용하여 얻은 값을 사용하여 reflect.Value · SetInt

    필드 이름 "id"및 "name"이 마음에 들지 않는다고 가정하여 "Id"및 "Name"으로 이름이 변경되었습니다.

    a)이 가정이 맞습니까?

    b) 맞다면 같은 파일 / 패키지에서 필요하지 않다고 생각했습니다.

  4. 필드 i의 설정 값-두 번 시도 (필드 이름이 대문자로 표시됨)-패닉

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

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

    panic : 주소를 지정할 수없는 값을 사용하는 reflect.Value · SetInt


@peterSO의 아래 지침은 철저하고 고품질입니다.

네. 이것은 작동합니다 :

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

그는 필드 이름을 내보낼 수 있어야한다고 문서화합니다 (대문자로 시작).


reflect데이터를 설정하는 데 사용할 수있는 가장 가까운 예 는 comments.gmane.org/gmane.comp.lang.go.general/35045 였지만 그곳에서도 json.Unmarshal실제 더러운 작업을했습니다
cc young

(위의 코멘트는 쓸모가 없습니다)
cc young

답변:


156

Go는 오픈 소스 코드 로 제공됩니다 . 리플렉션에 대해 배우는 좋은 방법은 핵심 Go 개발자가 리플렉션을 사용하는 방법을 확인하는 것입니다. 예를 들어 Go fmtjson 패키지가 있습니다. 패키지 문서에는 패키지 파일 제목 아래에 소스 코드 파일에 대한 링크가 있습니다.

Go json 패키지는 Go 구조에서 JSON을 마샬링하고 언 마샬링합니다.


다음은 struct오류를 조심스럽게 피하면서 필드 값을 설정하는 단계별 예제입니다 .

Go reflect패키지에는 CanAddr기능이 있습니다.

func (v Value) CanAddr() bool

Addr로 값의 주소를 얻을 수있는 경우 CanAddr은 true를 반환합니다. 이러한 값을 주소 지정 가능이라고합니다. 값은 슬라이스의 요소, 주소 지정 가능한 배열의 요소, 주소 지정 가능한 구조체의 필드 또는 포인터 역 참조의 결과 인 경우 주소를 지정할 수 있습니다. CanAddr이 false를 반환하면 Addr을 호출하면 패닉이 발생합니다.

이동의 reflect패키지는이 CanSet경우, 기능, true즉 의미 CanAddr도입니다 true.

func (v Value) CanSet() bool

CanSet은 v 값을 변경할 수있는 경우 true를 반환합니다. 값은 주소 지정이 가능하고 내 보내지 않은 구조체 필드를 사용하여 얻지 못한 경우에만 변경할 수 있습니다. CanSet이 false를 반환하면 Set 또는 유형별 setter (예 : SetBool, SetInt64)를 호출하면 패닉이 발생합니다.

우리 Setstruct현장에서 할 수 있는지 확인해야 합니다. 예를 들면

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

모든 오류 검사가 필요하지 않다고 확신 할 수 있다면 예제는 다음과 같이 단순화됩니다.

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
포기! 어딘가에 대답이 있지만 json pkg에 대한 4 시간의 작업으로 나에게 답이 없습니다. re the reflect pkg, pull info는 매우 간단하지만 데이터를 설정하려면 어딘가에서 간단한 예제를보고 싶은 흑 마법이 필요합니다!
cc young

2
훌륭해! 태국에 계시다면 맥주 한두 잔 마시 게 해주세요! 대단히 감사합니다
젊은 CC

5
훌륭한 실제 예,이 기사는 나를 위해 완전히 이해하지 못했습니다. golang.org/doc/articles/laws_of_reflection.html
danmux

최고 example.Here는 동일한 코드의 놀이터 샘플입니다 play.golang.org/p/RK8jR_9rPh은
Sarath Sadasivan 필라

이것은 구조체 필드를 설정하지 않습니다. 이것은 struct에 대한 포인터의 필드를 설정합니다. 분명히 이것은 OP가 묻는 질문에 대한 대답이 아닙니다.
wvxvw

13

이것은 작동하는 것 같습니다.

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()))
}

인쇄물:

123
321

감사! 이제 peterSO의 노트를 읽었으므로 이것은 완벽하게 이해됩니다. 나는 & foo가 아니라 foo를 사용하고 있었기 때문에 변경할 수 없었고 Elem ()이 무엇인지 확신 할 수 없었습니다.
cc young
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.