反射(reflect)的使用

package main

import (
   "fmt"
   "reflect"
)

// Human 定义一个名为Human的结构体
type Human struct {
   Name   string // 姓名
   Age    int    // 年龄
   Gender string // 性别
}

// GetName Human结构体的GetName()方法
func (hum Human) GetName() string {
   return hum.Name
}

// GetAge Human结构体的GetAge()方法
func (hum Human) GetAge() int {
   return hum.Age
}

// GetGender Human结构体的GetGender()方法
func (hum Human) GetGender() string {
   return hum.Gender
}

func reflectTest(v interface{}) {
   reflectType := reflect.TypeOf(v)
   reflectTypeKind := reflectType.Kind()
   reflectTypeKindString := reflectTypeKind.String()
   fmt.Printf("reflectType = %+v \n", reflectType)                     // reflectType = *main.Human
   fmt.Printf("reflectTypeKind = %+v \n", reflectTypeKind)             // reflectTypeKind = ptr
   fmt.Printf("reflectTypeKindString = %+v \n", reflectTypeKindString) // reflectTypeKindString = ptr

   reflectValue := reflect.ValueOf(v)
   reflectValueKind := reflectValue.Kind()
   reflectValueKindString := reflectValueKind.String()
   fmt.Printf("reflectValue = %+v \n", reflectValue)                     // reflectValue = &{Name:张三 Age:18 Gender:男}
   fmt.Printf("reflectValueKind = %+v \n", reflectValueKind)             // reflectValueKind = ptr
   fmt.Printf("reflectValueKindString = %+v \n", reflectValueKindString) // reflectValueKindString = ptr

   reflectValueElem := reflectValue.Elem()
   fmt.Printf("reflectValueElem = %+v \n", reflectValueElem) // reflectValueElem = {Name:张三 Age:18 Gender:男}

   reflectValueElemKind := reflectValueElem.Kind()
   fmt.Printf("reflectValueElemKind = %+v \n", reflectValueElemKind) // reflectValueElemKind = struct

   if reflectValueElemKind == reflect.Struct {
      fmt.Println("形参v是结构体") // 形参v是结构体

      // 获取结构体有几个字段,并遍历这些字段
      numField := reflectValueElem.NumField()
      for i := 0; i < numField; i++ {
         fmt.Printf("第%+v个字段的值为:%+v \n", i, reflectValueElem.Field(i))
      }
      // 第0个字段的值为:张三
      // 第1个字段的值为:18
      // 第2个字段的值为:男

      // 获取结构体有几个方法,并调用一遍这些方法(重要提醒:方法是按ASCII码升序的,不是按定义的先后顺序)
      numMethod := reflectValueElem.NumMethod()
      for i := 0; i < numMethod; i++ {
         ret := reflectValueElem.Method(i).Call(nil) // Call()方法的参数即为结构体方法的参数
         fmt.Printf("第%+v个方法的返回值为:%+v \n", i, ret[0])
      }
      // 第0个方法的返回值为:18
      // 第1个方法的返回值为:男
      // 第2个方法的返回值为:张三

      // 检查指定方法是否定义
      methodName := "GetWeight" // 未定义的方法名
      isValid := reflectValueElem.MethodByName(methodName).IsValid()
      if isValid {
         fmt.Printf("%+v()方法存在 \n", methodName)
      } else {
         fmt.Printf("%+v()方法不存在 \n", methodName) // GetWeight()方法不存在
      }

      // 检查指定方法是否定义
      methodName = "GetName" // 已定义的方法名
      isValid = reflectValueElem.MethodByName(methodName).IsValid()
      if isValid {
         fmt.Printf("%+v()方法存在 \n", methodName) // GetName()方法存在

         ret := reflectValueElem.MethodByName(methodName).Call(nil)
         fmt.Printf("利用反射调用%+v()方法,返回值为:%+v \n", methodName, ret[0])
         // 利用反射调用GetName()方法,返回值为:张三
      } else {
         fmt.Printf("%+v()方法不存在 \n", methodName)
      }

      // 检查指定字段是否存在
      fieldByName := "Weight" // 不存在的字段
      fieldIsValid := reflectValueElem.FieldByName(fieldByName).IsValid()
      if fieldIsValid {
         fmt.Printf("%+v字段存在 \n", fieldByName)
      } else {
         fmt.Printf("%+v字段不存在 \n", fieldByName) // Weight字段不存在
      }

      // 检查指定字段是否存在
      fieldByName = "Age" // 存在的字段名
      fieldIsValid = reflectValueElem.FieldByName(fieldByName).IsValid()
      if fieldIsValid {
         fmt.Printf("%+v字段存在 \n", fieldByName) // Age字段存在

         // 利用反射修改结构体字段的值(重要提醒:字段名首字母需大写才能利用反射修改其值)
         field := reflectValueElem.FieldByName(fieldByName)
         fieldKindString := field.Kind().String()
         if fieldKindString == "int64" || fieldKindString == "int" {
            field.SetInt(17)
         }
      } else {
         fmt.Printf("%+v字段不存在 \n", fieldByName)
      }
   } else {
      fmt.Println("形参v不是结构体")
   }
}

func main() {
   hum := Human{
      Name:   "张三",
      Age:    18,
      Gender: "男",
   }

   reflectTest(&hum)

   fmt.Printf("hum = %+v \n", hum) // hum = {Name:张三 Age:17 Gender:男}
   // 由于利用反射修改了Age字段的值,故其值不是18,而是17
}

//========== 总结 ==========//
// 1、反射属于高级应用,一般开发用不上,通常只有开发框架才会使用反射(在其它语言里也一样,反射多用于开发框架)。

Copyright © 2024 码农人生. All Rights Reserved