怎么给网站添加图标推广普通话标语
文章目录
- 获取类型和值
- 获取属性的类型和值
- 通过反射修改值
- 获取方法的名称和类型
- 调用方法
- 反射的缺点
获取类型和值
- 之前讲过接口
nil不一定等于空接口,因为一个 interface 底层 由type+value构成,只有type和value都匹配,才能== reflect.VlaueOf就是用来获取具体的reflect.Valuereflect.TypeOf用来获取具体的reflect.Type
func main() {var (a *Ab interface{})fmt.Println(a)if b == nil {fmt.Println("b is nil")}fmt.Println(reflect.TypeOf(b), reflect.ValueOf(b))fmt.Println(reflect.TypeOf(a), reflect.ValueOf(a))b = aif b == nil {fmt.Println("b is nil")} else {fmt.Printf("current b is %v \n", b)fmt.Println("b not eq nil")}fmt.Println(reflect.TypeOf(b), reflect.ValueOf(b))
}

上面的代码说明了,刚开始的空接口 == nil,后来的接口为啥不等于 nil,因为 type变了,虽然value 还是 nil
获取属性的类型和值
- 通过
reflect.Value或者reflect.Type的NumField获取属性数量 - 通过
reflect.Type的Field 方法获取属性相关信息 - 通过
reflect.Value的Field 方法获取值相关信息
package mainimport ("fmt""reflect"
)type A struct {Name stringAge int
}func main() {var a AgetType := reflect.TypeOf(a)getValue := reflect.ValueOf(a)fmt.Println("field num", getType.NumField())for i := 0; i < getType.NumField(); i++ {field := getType.Field(i)value := getValue.Field(i)fmt.Println("field name is", field.Name, "field value is", value.Interface())}
}

通过反射修改值
- 通过获取value的反射对象即可,
reflect.ValueOf传入的必须是指针类型,只有原始反射对象可以进行修改,可以通过reflect.Value的Elem方法取得 - 通过
reflect.Value的Canset方法来判断是否可以设置 - 通过
Set...系列方法来设置具体类型的值
package mainimport ("fmt""reflect"
)type A struct {Name stringAge int
}func main() {a := A{Name: "old name",}valueOfA := reflect.ValueOf(&a).Elem()nameField := valueOfA.Field(0)if nameField.CanSet() {nameField.SetString("new name")} else {fmt.Println("don't set")}fmt.Println("new value", a.Name)
}
- 因为调用
set...设置值,需要知道类型,可以通过reflect.Type的kind方法获取原始类型- 再通过 switch 去匹配类型来调用具体的
set...方法
- 再通过 switch 去匹配类型来调用具体的
package mainimport ("fmt""reflect"
)type A struct {Name stringAge int
}func main() {a := A{Name: "old name",}fmt.Println("old value", a.Name)valueOfA := reflect.ValueOf(&a).Elem()getType := reflect.TypeOf(a)field := getType.Field(0)nameField := valueOfA.Field(0)if nameField.CanSet() {switch field.Type.Kind() {case reflect.String:fmt.Println("string")nameField.SetString("new value")}} else {fmt.Println("don't set")}fmt.Println("new value", a.Name)
}

获取方法的名称和类型
- 先通过
reflect.Type的NumMethod方法获取方法数量 - 在通过
reflect.Type的Method方法获取到具体的方法信息reflect.Method
package mainimport ("fmt""reflect"
)type A struct {Name stringAge int
}func (receiver *A) SetName(name string) {receiver.Name = name
}func (receiver *A) SetAge(age int) {receiver.Age = age
}func main() {var a A//有方法是依赖指针的所以需要传指针getType := reflect.TypeOf(&a)num := getType.NumMethod()for i := 0; i < num; i++ {method := getType.Method(i)fmt.Println("method name:", method.Name, "method type:", method.Type)}
}

调用方法
- 通过
reflect.Method的Call方法即可调用反射对象的方法- Call 中 接收的参数为
reflect.Value的切片 - 如果反射对象的方法不需要参数,传一个
reflect.Value的空切片即可 - 如果反射对象需参数,那么需要由反射对像参数的
reflect.Value组成切片,传入Call完成调用
- Call 中 接收的参数为
package mainimport ("fmt""reflect"
)type A struct {Name stringAge int
}type Body struct {Like stringDesc string
}func (a A) Pr() {fmt.Println("A pr")
}func (a A) Talk(b Body) {fmt.Printf("Like:%s,Desc:%s", b.Like, b.Desc)
}func main() {var a AgetType := reflect.ValueOf(a)pr := getType.Method(0)//不需要参数pr.Call([]reflect.Value{})b := Body{Like: "i'm like",Desc: "i'm desc",}talk := getType.Method(1)//Talk 需要传入 Body struct, 所以反射调用,需要传入 由 Body的 reflect.Value 组成切片参数talk.Call([]reflect.Value{reflect.ValueOf(b),})
}

反射的缺点
- 反射慢
- 不管什么编程语言,反射都慢
- 反射实现里有对
reflect.kind大量的枚举 + 类型转换 等操作 reflect.Value不能复用,每次都是返回一个新的值,其中 typ 还是指针类型,涉及对指针的频繁分配,GC



