구조체는 리스트에 비해서 명확하고 코드의 가독성을 높여주기에 애용하는 편이다.
또한 여러 타입의 변수를 하나에 묶어둘 수 있다는 것도 장점이다.
귀찮은 점은 list 나 array는 for문으로 대충 쭉 훑는게 가능한데 구조체는 그게 간단하지가 않다.
하지만 그렇다고 전체가 필요할 때 구조체의 필드명을 하나하나 말하고 있으면 코드가 길어지고 확장성이 떨어진다.
이번 Golang으로 하는 작업 중에 Field를 열거해 줄 일이 있었다.
Golang은 깐깐해 보이면서도 Interface라는 상당히 유연한 시스템을 가지고 있다. 먼저 다음의 코드를 살펴보자
package main
import (
"fmt"
"reflect"
)
type Person struct {
Name string
City string
Age int
}
func main() {
s := Person{"EBeb", "Hoshimi", 17}
v := reflect.ValueOf(s)
type_of_fields := v.Type()
for i := 0; i < v.NumField(); i++ {
fmt.Printf(" %s : %v\n", type_of_fields.Field(i).Name, v.Field(i).Interface())
}
}
출력
Name : EBeb
City : Hoshimi
Age : 17
Program exited.
reflect에는 변수형을 다루는 여러가지 함수들이 있는 것 같다. 종종 보인다.
먼저 reflect.ValueOf 에 관한 설명이다.
Type은 이 구조체를 Type형으로 return해주는 역할을 한다. Field는 index상에 있는 Field의 값을 구조체에 담아준다.
즉 이 Field의 값이 뭐가 나올 지 모르기 때문에 여러 변수형들에 대한 구조체를 준비해서 적절한 곳에 대입한다.
Interface를 사용하여 여기서 실제 값을 그 타입에 맞게 반환시킨다.
공식문서 상에서 Type안의 .Field의 설명이다
// Field returns a struct type's i'th field.
// It panics if the type's Kind is not Struct.
// It panics if i is not in the range [0, NumField()).
Field(i int) StructField
그럼 Return하는 StructField는 무슨 구조일까
type StructField struct {
// Name is the field name.
Name string
// PkgPath is the package path that qualifies a lower case (unexported)
// field name. It is empty for upper case (exported) field names.
// See https://golang.org/ref/spec#Uniqueness_of_identifiers
PkgPath string
Type Type // field type
Tag StructTag // field tag string
Offset uintptr // offset within struct, in bytes
Index []int // index sequence for Type.FieldByIndex
Anonymous bool // is an embedded field
}
음 Field에 대한 정보들을 가져올 수 있다. Name으로 그 Field의 이름이나 Type으로 그 Field의 타입을 가져올 수 있다.