简体   繁体   中英

Using reflection to iterate over struct's struct members and calling a method on it

I have a struct that has one or more struct members. Each member is expected to implement a Validator interface.

I want to use reflection to iterate over all struct members and call the interface's Validate() method. For example:

package main

import "fmt"
import "reflect"

type Validator interface {
    Validate()
}

type T1 struct {
    S string
}

func (p *T1) Validate() {
    fmt.Println("HERE 1")
}

type T2 struct {
    S string
}

func (p *T2) Validate() {
    fmt.Println("HERE 2")
}

type Top struct {
    S1 T1
    S2 T2
}

func main() {
    var t Top

    r := reflect.ValueOf(t)
    for i := 0; i < r.NumField(); i++ {
        f := r.Field(i)
        if f.Kind() == reflect.Struct {
            validator := f.Interface().(Validator)
            validator.Validate()
        }
    }
}

When run, it outputs:

panic: interface conversion: main.T1 is not main.Validator: missing method Validate

If I change the Validate() methods to accept value (rather than pointer) receivers, then it works. However, I want to use pointer receivers since the struct s may grow to be large.

How can I change the reflection code to work where the methods are defined taking pointer receivers?

I also tried using this line:

            validator := f.Addr().Interface().(Validator)

to get a pointer, but it then outputs:

panic: reflect.Value.Addr of unaddressable value

None of your values are addressable, so you can't call any methods on them with pointer receivers.

If the S1 and S2 fields can't be pointers, you can still address them if you have a pointer to the Top struct:

r := reflect.ValueOf(&t).Elem()
for i := 0; i < r.NumField(); i++ {
    f := r.Field(i)
    if f.Kind() == reflect.Struct {
        validator := f.Addr().Interface().(Validator)
        validator.Validate()
    }
}

"Top.S1" is of type "T1" and is different from "*T1" type defined "Validate()".

If you change "Top.S1" to "*T1" type and modify the field type checking, your code will work properly.

type Top struct {
    S1 *T1
    S2 *T2
}

func main() {
    t := Top{&T1{}, &T2{}}

    r := reflect.ValueOf(t)
    for i := 0; i < r.NumField(); i++ {
        f := r.Field(i)
        if f.Kind() == reflect.Ptr && f.Elem().Kind() == reflect.Struct {
            validator, ok := f.Interface().(Validator)
            if ok {
                validator.Validate()
            } else {
                fmt.Println("not ok")
            }
        }
    }
}

https://play.golang.org/p/hHqP6WdMYqT

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM