黄宽的blog

宁可十年不将军,不可一日不拱卒



反射reflection

type User struct {
    Name string
    Age  int
    Id   int
}

func (u User) GetName(name string) { //将匿名方法绑定到结构体中
    fmt.Println("我的名字叫", u.Name, "传进来的是", name)
}

func main() {
    a := User{"xiaoming", 5, 6}
    a.GetName("测试1")
    Setname(&a)
    a.GetName("测试2")
    fmt.Println(a.Name)
    info(a)
    ra := reflect.ValueOf(a)
    rm := ra.MethodByName("GetName")
    args := []reflect.Value{reflect.ValueOf("测试三")} //通过反射调用该对象方法
    //这里再次回到切片与数组。到底什么是切片,什么是数组
    //再次总结一下,刚看了点资料,以下是链接
    //https://www.zhihu.com/question/66673454
    //以下是总结
    //1.数组要指明长度Array := [ArrayLength] ElementType,arr :=[4]int,数组拷贝为值拷贝
    //2.切片不需要指明长度SliceT := []ElementType,但是他有容量机制,拷贝为指针拷贝
    arr := [4]int{0, 1, 3, 4}
    arrs := arr[1:2]
    ccc := reflect.TypeOf(arrs).Kind() //通过反射判断是一个数组还是切片
    fmt.Println(ccc)
    rm.Call(args)
}

func info(o interface{}) { //获取这个实例化了的结构体的所有信息
    ot := reflect.TypeOf(o)  //通过反射获取类型
    ov := reflect.ValueOf(o) //获取值
    fmt.Println(ot, "\n")
    fmt.Println(ov, "\n")
    fmt.Println(ot.Name(), "\n")            //打印这个实现了空接口的结构名称
    for i := 0; i < ot.NumField(); i += 1 { //获取这个结构体内的值
        n := ot.Field(i)
        val := ov.Field(i)
        fmt.Println(n.Type, ":", n.Name, ",", val)
    }
    for i := 0; i < ot.NumMethod(); i += 1 { //打印这个结构体的每个方法
        f := ot.Method(i)
        fmt.Printf("%s:%v,%v\n", f.Name, f.Type, f.Index)
    }

}

func Setname(o interface{}) { //通过反射设置值
    v := reflect.ValueOf(o)
    fmt.Println(v.Elem())          //这里打印出来是这个结构体
    fmt.Println(v.Elem().CanSet()) //这里返回的是个布尔
    fmt.Println(v.Kind())          //这里打印出来的就是ptr
    fmt.Println(reflect.Ptr)       //也是ptr,引出个问题,为什么不用字符串"ptr"?以后再找答案,指针
    fmt.Println(v)
    //Elem返回v持有的接口保管的值的Value封装,
    //或者v持有的指针指向的值的Value封装。如果v的Kind不是Interface或Ptr会panic;如果v持有的值为nil,会返回Value零值。
    //Kind返回v持有的值的分类,如果v是Value零值,返回值为Invalid
    if v.Kind() == reflect.Ptr && !v.Elem().CanSet() { //如果是指针且不能被修改
        fmt.Println("xxx") //这个判断有问题,如果不是指针也会走else,或者可以修改但不是指针也会走else
        return
    } else {
        v = v.Elem()
    }
    n := v.FieldByName("Name")
    if n.IsValid() && n.Kind() == reflect.String { //如果取到了值并且类型是字符串则修改
        fmt.Println(n, "\n", n.Kind())
        n.SetString("lele") //修改名字//通过反射对值进行修改,肯定可以修改拉,传进来的是指针
    }
}

下面是执行结果

我的名字叫 xiaoming 传进来的是 测试1

{xiaoming 5 6}

true

ptr

ptr

&{xiaoming 5 6}

xiaoming

string

我的名字叫 lele 传进来的是 测试2

lele

main.User


{lele 5 6}


User


string : Name , lele

int : Age , 5

int : Id , 6

GetName:func(main.User, string),0

slice

我的名字叫 lele 传进来的是 测试三

成功: 进程退出代码 0.


  golang

作者  :  黄宽

不耻最后,即使慢,驰而不息,纵会落后,纵令失败



About ME

about me

黄宽

我不想成为一个庸俗的人。十年百年后,当我们死去,质疑我们的人同样死去,后人看到的是裹足不前、原地打转的你,还是一直奔跑、走到远方的我?

友情链接