Golang: an example of the use of reflect reflection

insert image description here

1. The function of reflect package

The reflect package defines "reflection" related capabilities. "Reflection" in computer science refers to the ability of a computer program to access, detect, and modify its own state or behavior at runtime (runtime). Based on the reflection feature, some problems that require frequent code modification and hard coding can be solved universally, but the execution efficiency will be reduced.

2. Core APIs

Refer to the official documentation: https://pkg.go.dev/reflect#pkg-functions

3. Use cases

3.1. Type judgment

  • Purpose: to perform specific processing after judging that the interface belongs to a certain type
  • Main usage: TypeOf, Kind
type User struct {
    
    
    Name string
    Age  int
}

type IUserService interface {
    
    
    GetUser(userID int64) User
}

func reflectType() {
    
    
    var (
        intType       int
        stringType    *string
        mapType       map[string]interface{
    
    }
        structType    *User
        interfaceType IUserService
    )
    var types []interface{
    
    }
    types = append(types, intType)
    types = append(types, stringType)
    types = append(types, mapType)
    types = append(types, structType)
    types = append(types, interfaceType)

    // 类型判断
    for i, v := range types {
    
    
        rtyp := reflect.TypeOf(v)
        if rtyp == nil {
    
    
            fmt.Printf("(%v)类型获取Type为nil,跳过\n", i)
            continue
        }
        rkind := rtyp.Kind()
        // 如果为指针类型,还原出真实类型
        if rtyp.Kind() == reflect.Ptr {
    
    
            fmt.Printf("(%v)指针类型: %v\n", i, rkind.String())
            rtyp = rtyp.Elem()
        }
        rkind = rtyp.Kind()
        fmt.Printf("(%v)类型%v\n", i, rkind.String())
    }
}

output:

(0)类型int
(1)指针类型: ptr
(1)类型string
(2)类型map
(3)指针类型: ptr
(3)类型struct
(4)类型获取Type为nil,跳过

3.2. Variable value acquisition

  • Used for: getting the value of a type
  • Main use: ValueOf, Kind
func reflectValue() {
    
    
    var (
        intType    int                    = 10
        stringType string                 = "hello world"
        mapType    map[string]interface{
    
    } = nil
    )
    var types []interface{
    
    }
    types = append(types, intType)
    types = append(types, stringType)
    types = append(types, mapType)
    // 类型判断
    for i, v := range types {
    
    
        rval := reflect.ValueOf(v)
        if rval.Kind() == reflect.Ptr {
    
    
            rval = rval.Elem()
        }
        fmt.Printf("(%v)的值%v\n", i, rval.Interface())
    }
}

output:

(0)的值10
(1)的值hello world
(2)的值map[]

3.3. Traverse the fields and values ​​of the structure

  • Purpose: to traverse the fields and values ​​of unknown type structures, avoiding hard-coded structure processing
  • Main use: TypeOf, ValueOf, NumField
func rangeStruct() {
    
    
    u := User{
    
    
        Name: "arong",
        Age:  23,
        Favors: []Favor{
    
    
            {
    
    
                Name: "篮球",
                ID:   1,
            },
            {
    
    
                Name: "唱跳",
                ID:   2,
            },
            {
    
    
                Name: "RAP",
                ID:   3,
            },
        },
    }
    rval := reflect.ValueOf(&u)
    if rval.Kind() == reflect.Ptr {
    
    
        rval = rval.Elem()
    }
    for i := 0; i < rval.NumField(); i++ {
    
    
        name := rval.Type().Field(i).Name
        val := rval.Field(i).Interface()
        fmt.Printf("字段%v值为%v\n", name, val)
    }
}

output:

字段Name值为arong
字段Age值为23
字段Favors值为[{篮球 1} {唱跳 2} {RAP 3}]

3.4. Structure field assignment

  • Purpose: Assign values ​​to specific fields of unknown structures
  • Main use: ValueOf, FieldByName, CanSet, Set
func reflectSetValue() {
    
    
    u := User{
    
    
        Name: "arong",
        Age:  23,
    }
    fmt.Printf("原始字段值:%#v\n", u)

    // 一定要取指针,不然无法赋值
    rval := reflect.ValueOf(&u).Elem()
    rvalNameField := rval.FieldByName("Name")
    // 字段是否可写入
    if rvalNameField.CanSet() {
    
    
        rvalNameField.Set(reflect.ValueOf("pbrong"))
    }

    fmt.Printf("改变已知字段值:%#v\n", u)
}
3.5.函数及方法调用
- 用途:使用反射动态调用指定函数及方法
- 主要使用:
type User struct {
    
    
    Name   string
    Age    int
    Favors []Favor
}

type IUserService interface {
    
    
    GetUser(userID int64) User
}

type UserService struct {
    
    
}

func (u *UserService) GetUser(userID int64) User {
    
    
    user := User{
    
    
        Name:   "defaultTestUser",
        Age:    -1,
        Favors: []Favor{
    
    },
    }
    fmt.Printf("GetUser exec: result = %#v\n", user)
    return user
}

func TestFunc(names string) {
    
    
    fmt.Printf("TestFunc exec: result = %v\n", names)
}

func reflectSetValue() {
    
    
    u := User{
    
    
        Name: "arong",
        Age:  23,
    }
    fmt.Printf("原始字段值:%#v\n", u)

    // 一定要取指针,不然无法赋值
    rval := reflect.ValueOf(&u).Elem()
    rvalNameField := rval.FieldByName("Name")
    // 字段是否可写入
    if rvalNameField.CanSet() {
    
    
        rvalNameField.Set(reflect.ValueOf("pbrong"))
    }

    fmt.Printf("改变已知字段值:%#v\n", u)
}

output:

TestFunc exec: result = pbrong
GetUser exec: result = main.User{Name:"defaultTestUser", Age:-1, Favors:[]main.Favor{}}
GetUser resp: main.User{Name:"defaultTestUser", Age:-1, Favors:[]main.Favor{}}

4. Avoid abusing reflection

Improper use of reflection can affect program performance and readability. Therefore, in Go, we generally recommend avoiding abuse of reflection as much as possible.
The main reason is that the essence of reflection is a type conversion at runtime, which will cause certain overhead. Because it needs to dynamically obtain type information, perform type checking, and dynamically allocate memory at runtime. These operations require more computing resources and time than static type conversion.
In addition, because the use of reflection is not intuitive and concise, it may reduce the readability and maintainability of the code. Especially when developers don't understand how reflection works, it is easy to have some problems that are difficult to debug and troubleshoot.
Therefore, in general, we should avoid abusing reflection as much as possible. Only use reflection in necessary scenarios, such as dynamically creating objects, calling functions, parsing data, etc. at runtime. In other scenarios, we should use Go's static type system and language features as much as possible to make the code more concise, efficient and readable.

Guess you like

Origin blog.csdn.net/pbrlovejava/article/details/129118211