reflect 修改map slice struct

#反射操作切片
通过 反射 修改 切片 的值,分为两种情况,第一种是整体修改整个切片的值,第二种是单独修改切片的某个索引处的值。



整体修改切片的值,我们使用 Set 函数,传入整个切片即可,修改某个索引处的值,我们首先需要通过 Index 找到索引,然后使用 Set 函数做修改。



反射修改整个切片
语法
intSliceElemValue := reflect.ValueOf(&intSlice).Elem()
newVale := reflect.ValueOf(newSliceValue)
intSliceElemValue.Set(newVale)
说明
首先,我们使用 reflect.ValueOf 传入我们要修改的切片的地址,并且使用 Elem 获取指针信息,接着,我们使用 reflect.ValueOf 传入一个新的切片。



最后,我们调用 Set 方法,传入新的切片的 Value 信息,即可实现修改原来的切片的值。



反射修改切片索引处的值
语法
intSliceValue := reflect.ValueOf(intSlice)
e := intSliceValue.Index(0)
e.SetInt(2560)
说明
首先,我们使用 reflect.ValueOf 传入我们要修改的切片,注意,这里传入的是切片,而不是切片的地址,接着,我们使用 Index 获取需要修改的索引处的值。



最后,我们调用 Set 方法,传入新的值,即可实现修改切片某个索引处的值。



https://haicoder.net/golang/golang-reflect-valueof.html

#Go语言反射解析结构体
在 Golang 中,通过 反射 的 reflect.TypeOf() 获得反射的对象信息后,如果是 结构体 类型,可以通过反射值对象(reflect.Type)的 NumField() 和 Field() 方法获得结构体成员的详细信息。



person := Person{
Name:”HaiCoder”,
Age:109,
}
personType := reflect.TypeOf(person)
for i := 0; i < personType.NumField(); i++ {
// 获取每个成员的结构体字段类型
fieldType := personType.Field(i)
// 输出字段名
fmt.Println(“FiledName =”, fieldType.Name)
}
https://haicoder.net/golang/golang-reflect-struct.html



#反射(reflect)访问的 map
slice 索引表达式是可寻址的(例如,&s[0] 是有效的),因此通过反射获得的 slice 元素将是可设置的。映射索引表达式是 而不是 可寻址的(例如 &m[“a”] 无效),因此通过反射获得的键的值将不可设置。查看相关How to update map values in Go



只有可寻址的值是可设置的,尝试“设置”不可寻址的值只能修改副本(而不是原始值),因此首先不允许这样做。引用 Value.CanSet()



如果要使用反射更改分配给映射中键的值,请使用 Value.SetMapIndex() 方法:
mv.SetMapIndex(reflect.ValueOf(“a”), reflect.ValueOf(“11”))



https://article.docway.net/it/details/60f0fb797919366004f69814



golang 里面的所有函数调用都是值复制,所以这里在调用 reflect.ValueOf 的时候,已经复制了一个 x 传递进去了,这里获取到的 v 是一个 x 复制体的 value。那么这个时候,我们就希望知道我能不能通过 v 来设置这里的 x 变量。就需要有个方法来辅助我们做这个事情: CanSet ()



这个指针指向的元素是否可以设置,所幸 reflect 提供了 Elem () 方法来获取这个 “指针指向的元素”。
var x float64 = 3.4
v := reflect.ValueOf(&x)
fmt.Println(v.CanSet()) // false



var x float64 = 3.4
v := reflect.ValueOf(&x)
fmt.Println(v.Elem().CanSet()) // true



CanAddr
在 reflect 包里面可以看到,除了 CanSet 之外,还有一个 CanAddr 方法。它们两个有什么区别呢?



CanAddr 方法和 CanSet 方法不一样的地方在于:对于一些结构体内的私有字段,我们可以获取它的地址,但是不能设置它。



CanAddr 是 CanSet 的必要不充分条件。一个 Value 如果 CanAddr, 不一定 CanSet。但是一个变量如果 CanSet,它一定 CanAddr。



https://learnku.com/articles/51004



当需要处理处理动态数据结构时,我们无法在编译阶段就知道未知数据的结构,其中一个非常经典的使用情景就是对Json串的Marshal。此时,就该reflect包出场了,它提供了在运行时创建、更新某种类型以及获取该类型的各种信息的能力,有了它,我们不仅能有效处理动态数据类型,还可以大大提高代码的复用性、可读性。



Type
在reflect包中,是用Type来描述Go中某个对象的类型,并提供了一系列方法,来获取类型的相关信息,一般通过调用TypeOf来获取一个任意变量的类型Type



Value
而Value描述了在Go运行时某个对象的值,我们可以针对它进行增删改查之类的操作,一般通过ValueOf方法来获取对象的Value。



动态初始化结构体
unc NewDS() *DS {
ds := &DS{}
initStruct(ds)
fmt.Printf(“FieldOne = %s”, ds.FieldOne)
return ds
}



func initStruct(v interface{}) error {
e := reflect.Indirect(reflect.ValueOf(v))
if e.Kind() != reflect.Struct {
return errors.New(“v must be struct”)
}
et, ev := e.Type(), e
for i := 0; i < et.NumField(); i++ {
field, val := et.Field(i), ev.Field(i)
defaultValue, ok := field.Tag.Lookup(“default”)
if !ok {
continue
}
switch field.Type.Kind() {
case reflect.String:
val.SetString(defaultValue)
case reflect.Int:
if x, err := strconv.ParseInt(defaultValue, 10, 64); err != nil {
val.SetInt(x)
}
// 针对不同Kind,将defaultValue转换为对应类型并赋值

}
}
return nil
}



动态创建Map
通常情况下,我们是通过make来创建一个map,而有了reflect包后,我们也可以通过reflet包来动态地创建一个map。
func convert(rectangle *Rectangle) (res map[string]string, err error) {
e := reflect.Indirect(reflect.ValueOf(rectangle))
if e.Kind() != reflect.Struct {
return nil, errors.New(“v must be struct”)
}
et, ev := e.Type(), e



var mapStringType = reflect.TypeOf(make(map[string]string))
mapReflect := reflect.MakeMap(mapStringType)
for i := 0; i < et.NumField(); i++ {
field, val := et.Field(i), ev.Field(i)
switch field.Type.Kind() {
case reflect.String:
mapReflect.SetMapIndex(reflect.ValueOf(field.Name), reflect.ValueOf(val.String()))
case reflect.Float64:
s := strconv.FormatFloat(val.Float(), 'f', 2, 64)
mapReflect.SetMapIndex(reflect.ValueOf(field.Name), reflect.ValueOf(s))
// other cases
...
}
}
return mapReflect.Interface().(map[string]string), nil }


https://my.oschina.net/u/4339343/blog/4268374



一个可取地址的reflect.Value会记录一个结构体成员是否是未导出成员,如果是的话则拒绝修改操作。因此,CanAddr方法并不能正确反映一个变量是否是可以被修改的。另一个相关的方法CanSet是用于检查对应的reflect.Value是否是可取地址并可被修改的:



fmt.Println(fd.CanAddr(), fd.CanSet()) // “true false”



https://docs.hacknode.org/gopl-zh/ch12/ch12-05.html
https://www.kancloud.cn/wizardforcel/gopl-zh/106470



反射值对象修改值的方法
Set(x Value) 将值设置为传入的反射值对象的值
Setlnt(x int64) 使用 int64 设置值。当值的类型不是 int、int8、int16、 int32、int64 时会发生宕机
SetUint(x uint64) 使用 uint64 设置值。当值的类型不是 uint、uint8、uint16、uint32、uint64 时会发生宕机
SetFloat(x float64) 使用 float64 设置值。当值的类型不是 float32、float64 时会发生宕机
SetBool(x bool) 使用 bool 设置值。当值的类型不是 bod 时会发生宕机
SetBytes(x []byte) 设置字节数组 []bytes值。当值的类型不是 []byte 时会发生宕机
SetString(x string) 设置字符串值。当值的类型不是 string 时会发生宕机



值可修改条件之一:可被寻址
通过反射修改变量值的前提条件之一:这个值必须可以被寻址。
值可修改条件之一:被导出
结构体成员中,如果字段没有被导出,即便不使用反射也可以被访问,但不能通过反射修改



http://c.biancheng.net/view/116.html
https://www.yisu.com/zixun/452275.html


Category golang