[Go] Reflect three laws

김지환·2023년 4월 18일
0

Go

목록 보기
1/1

Tl;DR

Runtime 도중 인터페이스나 구조체의 타입을 확인하기 위해서 사용할 수 있는 문법 중 하나인 reflect에 대한 다양한 사용 방법을 정리해보자.

변수의 타입을 확인 하기.

reflect에는 2개의 타입이 존재하는데 Type, Value 이 두가지가 존재한다. 두 타입은 interface 변수에 대한 접근을 가능하게 한다. 각 타입을 얻는데는 reflect.TypeOf, reflect.ValueOf 가 사용된다.

Code

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var x float64 = 3.141592
    fmt.Println("type:", reflect.TypeOf(x))
    fmt.Println("type:", reflect.ValueOf(x))
}

Print:

type: float64
value: 3.141592

reflect.TypeOf(), reflect.ValueOf() 모두 인자로 any ( interface{} ) 타입을 받게 된다.

func TypeOf(i any) Type
func ValueOf(i any) Value

Interface() 를 통한 inverse기능.

Value Type은 Interface() 함수를 통해서 interface{} 타입으로 다시 되돌릴 수 있다.

Code

func main() {
	var x float64 = 3.141592
	v := reflect.ValueOf(x)
	c := v.Interface()
	if x == c {
		fmt.Println("Same")
	}
}

Print

Same

reflection object의 값을 변경하기

reflection을 통해 값을 받아오고 나서 변경을 하기 위해서는 주소값을 전달해줘야만 가능하다.
코드로 직접 보면 이해가 빠른데

Code

func main() {
	var x float64 = 3.141592
	// 주소값이 아닌 값을 그대로 전달하면 에러 발생
	v_1 := reflect.ValueOf(x)
	v_1.SetFloat(1.414)
}

Print

panic: reflect: reflect.Value.SetFloat using unaddressable value

unaddressable value 라는 에러를 발생시킨다. 값을 변경하고 싶다면 x의 address 값을 전달해주어야 한다. address를 전달하고 나서 코드를 실행해도 panic이 발생하게 되는데, 예를들어 포인터가 가리키는 주소의 값을 변경하기 위해서는 *를 사용해주어야 한다. 비슷하게 reflect의 Value에서는 Elem() 함수를 이용해서 값을 변경할 수 있다.

Code

func main() {
	var x float64 = 3.141592
	// 주소값이 아닌 값을 그대로 전달하면 에러 발생
	v := reflect.ValueOf(&x)
	p := v.Elem()
	p.SetFloat(1.414)
	fmt.Println(x)
}

Print

1.414

Struct 값 변경하기

struct의 field값을 변경하는 방법은 똑같이 ValueOf() 에 주소 값을 전달한다. 값을 변경하기 위해서는 ValueField, FieldByName 과 같은 함수를 통해서 변경이 가능하다. Field 는 struct에 있는 field 에 대해서 선언된 순서대로 접근이 가능하고 FieldByName은 field의 변수명을 통해서 접근이 가능하다.

Code

func main() {
	person := Human{"Jihwan", 23}
	person_value := reflect.ValueOf(&person).Elem()
	typeOfHuman := person_value.Type()
	for i := 0; i < person_value.NumField(); i++ {
		f := person_value.Field(i)
		fmt.Printf(
			"%d: %s %s = %v\n", i, typeOfHuman.Field(i).Name, f.Type(), f.Interface(),
		)
	}
	person_value.Field(0).SetString("Kim Jihwan")
	person_value.Field(1).SetInt(30)

	for i := 0; i < person_value.NumField(); i++ {
		f := person_value.Field(i)
		fmt.Printf(
			"%d: %s %s = %v\n", i, typeOfHuman.Field(i).Name, f.Type(), f.Interface(),
		)
	}

	person_value.FieldByName("Name").SetString("Kim")
	person_value.FieldByName("Age").SetInt(25)

	for i := 0; i < person_value.NumField(); i++ {
		f := person_value.Field(i)
		fmt.Printf(
			"%d: %s %s = %v\n", i, typeOfHuman.Field(i).Name, f.Type(), f.Interface(),
		)
	}
}

Print

0: Name string = Jihwan
1: Age int = 23
0: Name string = Kim Jihwan
1: Age int = 30
0: Name string = Kim
1: Age int = 25

Struct에 대한 field를 변경할 때 중요한 점은 field의 첫 문자는 대문자일 경우에만 변경이 가능하다는 점이다.

Reference

laws-of-reflection

profile
Developer

0개의 댓글