Loading...
Loading...

Go Reflection Tutorial

Reflection in Go (via the reflect package) enables programs to examine and manipulate objects at runtime. This powerful but advanced feature is used for tasks like serialization, ORMs, and dependency injection.

1. Reflection Basics

1.1 Type and Value

The core of reflection is the reflect.Type and reflect.Value types:

package main

import (
    "fmt"
    "reflect"
)

func main() {
    var x float64 = 3.14
    
    t := reflect.TypeOf(x)  // reflect.Type
    v := reflect.ValueOf(x) // reflect.Value
    
    fmt.Println("Type:", t)       // "float64"
    fmt.Println("Value:", v)      // "3.14"
    fmt.Println("Kind:", v.Kind()) // "float64"
}

Key Concepts:

  • TypeOf: Gets the static type information
  • ValueOf: Gets the runtime value information
  • Kind(): Returns the underlying primitive type

2. Inspecting Values

2.1 Working with Kinds

Different kinds require different handling:

func inspectValue(v reflect.Value) {
    switch v.Kind() {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        fmt.Println("Integer:", v.Int())
    case reflect.Float32, reflect.Float64:
        fmt.Println("Float:", v.Float())
    case reflect.String:
        fmt.Println("String:", v.String())
    case reflect.Bool:
        fmt.Println("Bool:", v.Bool())
    default:
        fmt.Println("Unsupported kind:", v.Kind())
    }
}

2.2 Accessing Struct Fields

Reflectively access struct fields and tags:

type User struct {
    Name string `json:"name"`
    Age  int    `json:"age"`
}

func main() {
    u := User{"Alice", 30}
    v := reflect.ValueOf(u)
    t := v.Type()
    
    for i := 0; i < v.NumField(); i++ {
        field := t.Field(i)
        value := v.Field(i)
        
        fmt.Printf("%s (%s) = %v, tag: %s\n",
            field.Name,
            field.Type,
            value.Interface(),
            field.Tag.Get("json"))
    }
}

3. Modifying Values

3.1 Settable Values

Values must be addressable to be modified:

func main() {
    var x float64 = 3.14
    v := reflect.ValueOf(&x).Elem() // Note: & and Elem()
    
    if v.CanSet() {
        v.SetFloat(6.28)
        fmt.Println(x) // 6.28
    }
}

3.2 Modifying Struct Fields

Change struct field values reflectively:

type Config struct {
    Host string
    Port int
}

func main() {
    cfg := &Config{"localhost", 8080}
    v := reflect.ValueOf(cfg).Elem()
    
    hostField := v.FieldByName("Host")
    if hostField.IsValid() && hostField.CanSet() {
        hostField.SetString("example.com")
    }
    
    fmt.Println(cfg) // &{example.com 8080}
}

4. Calling Functions

4.1 Dynamic Function Invocation

Call functions by name using reflection:

func Add(a, b int) int {
    return a + b
}

func main() {
    v := reflect.ValueOf(Add)
    args := []reflect.Value{
        reflect.ValueOf(3),
        reflect.ValueOf(5),
    }
    
    results := v.Call(args)
    sum := results[0].Int()
    fmt.Println(sum) // 8
}

4.2 Method Invocation

Call methods on structs dynamically:

type Calculator struct{}

func (c Calculator) Multiply(x, y int) int {
    return x * y
}

func main() {
    calc := Calculator{}
    v := reflect.ValueOf(calc)
    method := v.MethodByName("Multiply")
    
    result := method.Call([]reflect.Value{
        reflect.ValueOf(4),
        reflect.ValueOf(5),
    })
    
    fmt.Println(result[0].Int()) // 20
}

5. Advanced Patterns

5.1 Creating New Values

Instantiate types at runtime:

func main() {
    t := reflect.TypeOf(User{})
    v := reflect.New(t).Elem() // Creates zero value
    
    // Set fields
    v.FieldByName("Name").SetString("Bob")
    v.FieldByName("Age").SetInt(25)
    
    user := v.Interface().(User)
    fmt.Println(user) // {Bob 25}
}

5.2 Working with Slices and Maps

Dynamically manipulate collections:

func main() {
    // Create slice dynamically
    sliceType := reflect.SliceOf(reflect.TypeOf(0))
    slice := reflect.MakeSlice(sliceType, 0, 10)
    slice = reflect.Append(slice, reflect.ValueOf(1), reflect.ValueOf(2))
    
    // Create map dynamically
    mapType := reflect.MapOf(reflect.TypeOf(""), reflect.TypeOf(0))
    m := reflect.MakeMap(mapType)
    key := reflect.ValueOf("answer")
    value := reflect.ValueOf(42)
    m.SetMapIndex(key, value)
    
    fmt.Println(slice.Interface()) // [1 2]
    fmt.Println(m.Interface())     // map[answer:42]
}

6. Best Practices

6.1 When to Use Reflection

  • Appropriate uses: Serialization, ORMs, dependency injection
  • Avoid when: Static code would work, performance-critical paths

6.2 Performance Considerations

// ❌ Slow: Reflection in hot loop
func slowReflection(items []interface{}) {
    for _, item := range items {
        v := reflect.ValueOf(item)
        // ... reflection operations
    }
}

// ✅ Better: Cache reflect.Type and use type switches
var userType = reflect.TypeOf(User{})

func fastProcessing(items []interface{}) {
    for _, item := range items {
        switch v := item.(type) {
        case User:
            // Direct access
        case int:
            // Direct access
        }
    }
}

6.3 Safety Checks

func safeReflection(v reflect.Value) {
    // Always check these before operations:
    if !v.IsValid() {
        panic("Invalid value")
    }
    if v.Kind() != reflect.Int {
        panic("Wrong kind")
    }
    if !v.CanSet() {
        panic("Can't set value")
    }
}
0 Interaction
0 Views
Views
0 Likes
×
×
×
🍪 CookieConsent@Ptutorials:~

Welcome to Ptutorials

$ Allow cookies on this site ? (y/n)

top-home