Created
April 20, 2016 18:08
-
-
Save lpar/9e0431c1bc957a78cf3df90c8bf32617 to your computer and use it in GitHub Desktop.
A detailed example of reflection in Go, interrogating and modifying field values in a struct
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"reflect" | |
) | |
// A detailed example of examining a struct value in Go via reflection, | |
// and changing field values (if possible). | |
// | |
// See <http://lpar.ath0.com/2016/04/20/reflection-go-modifying-struct-values/> | |
// for accompanying article. | |
type Name string | |
type Person struct { | |
FirstName Name | |
LastName Name | |
Age int | |
} | |
func reflectOn(s interface{}) { | |
// Get the type and value of the argument we were passed. | |
ptyp := reflect.TypeOf(s) | |
pval := reflect.ValueOf(s) | |
// We can't do much with the Value (it's opaque), but we need it in order | |
// to fetch individual fields from the struct later. | |
var typ reflect.Type | |
var val reflect.Value | |
// If we were passed a pointer, dereference to get the type and value | |
// pointed at. | |
if ptyp.Kind() == reflect.Ptr { | |
fmt.Printf("Argument is a pointer, dereferencing.\n") | |
typ = ptyp.Elem() | |
val = pval.Elem() | |
} else { | |
fmt.Printf("Argument is %s.%s, a %s.\n", ptyp.PkgPath(), ptyp.Name(), | |
ptyp.Kind()) | |
typ = ptyp | |
val = pval | |
} | |
// Make sure we now have a struct | |
if typ.Kind() != reflect.Struct { | |
fmt.Printf("Not a struct.\n") | |
return | |
} | |
// Can we set values? | |
if val.CanSet() { | |
fmt.Printf("We can set values.\n") | |
} else { | |
fmt.Printf("We cannot set values.\n") | |
} | |
// The number of fields in the struct is determined by the type of struct | |
// it is. Loop through them. | |
for i := 0; i < typ.NumField(); i++ { | |
// Get the type of the field from the type of the struct. For a struct, | |
// you always get a StructField. | |
sfld := typ.Field(i) | |
// Get the type of the StructField, which is the type actually stored | |
// in that field of the struct. | |
tfld := sfld.Type | |
// Get the Kind of that type, which will be the underlying base type | |
// used to define the type in question. | |
kind := tfld.Kind() | |
// Get the value of the field from the value of the struct. | |
vfld := val.Field(i) | |
// Dump out what we've found | |
fmt.Printf("struct field %d: name %s type %s kind %s value %v\n", i, | |
sfld.Name, tfld, kind, vfld) | |
// Is that field some kind of string, and is the value one we can set? | |
if kind == reflect.String && vfld.CanSet() { | |
fmt.Printf("Overwriting field %s\n", sfld.Name) | |
// Assign to it | |
vfld.SetString("Anonymous") | |
} | |
} | |
} | |
func main() { | |
t := Person{"John", "Smith", 23} | |
fmt.Printf("First, passing the actual structure:\n\n") | |
reflectOn(t) | |
fmt.Printf("After reflection:\n%s %s, %d years old\n", | |
t.FirstName, t.LastName, t.Age) | |
fmt.Printf("\nNow, passing a pointer to the structure:\n\n") | |
reflectOn(&t) | |
fmt.Printf("After reflection:\n%s %s, %d years old\n", | |
t.FirstName, t.LastName, t.Age) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment