当前位置: 首页 > news >正文

苏州网络自学网站建设网站建设-易速通科技

苏州网络自学网站建设,网站建设-易速通科技,上海松江建设发展有限公司网站,现在的网站开发方式go 的反射是很脆弱的#xff0c;保证反射代码正确运行的前提是#xff0c;在调用反射对象的方法之前#xff0c; 先问一下自己正在调用的方法是不是适合于所有用于创建反射对象的原始类型。 go 反射的错误大多数都来自于调用了一个不适合当前类型的方法#xff08;比如在一…go 的反射是很脆弱的保证反射代码正确运行的前提是在调用反射对象的方法之前 先问一下自己正在调用的方法是不是适合于所有用于创建反射对象的原始类型。 go 反射的错误大多数都来自于调用了一个不适合当前类型的方法比如在一个整型反射对象上调用 Field() 方法。 而且这些错误通常是在运行时才会暴露出来而不是在编译时如果我们传递的类型在反射代码中没有被覆盖到那么很容易就会 panic。 本文就介绍一下使用 go 反射时很大概率会出现的错误。 获取 Value 的值之前没有判断类型 对于 reflect.Value我们有很多方法可以获取它的值比如 Int()、String() 等等。 但是这些方法都有一个前提就是反射对象底层必须是我们调用的那个方法对应的类型否则会 panic比如下面这个例子 var f float32 1.0 v : reflect.ValueOf(f) // 报错panic: reflect: call of reflect.Value.Int on float32 Value fmt.Println(v.Int())上面这个例子中f 是一个 float32 类型的浮点数然后我们尝试通过 Int() 方法来获取一个整数但是这个方法只能用于 int 类型的反射对象所以会报错。 涉及的方法Addr, Bool, Bytes, Complex, Int, Uint, Float, Interface调用这些方法的时候如果类型不对则会 panic。判断反射对象能否转换为某一类型的方法CanAddr, CanInterface, CanComplex, CanFloat, CanInt, CanUint。其他类型是否能转换判断方法CanConvert可以判断一个反射对象能否转换为某一类型。 通过 CanConvert 方法来判断一个反射对象能否转换为某一类型 // true fmt.Println(v.CanConvert(reflect.TypeOf(1.0)))如果我们想将反射对象转换为我们的自定义类型就可以通过 CanConvert 来判断是否能转换然后再调用 Convert 方法来转换 type Person struct {Name string }func TestReflect(t *testing.T) {p : Person{Name: foo}v : reflect.ValueOf(p)// v 可以转换为 Person 类型assert.True(t, v.CanConvert(reflect.TypeOf(Person{})))// v 可以转换为 Person 类型p1 : v.Convert(reflect.TypeOf(Person{}))assert.Equal(t, foo, p1.Interface().(Person).Name) }说明 reflect.TypeOf(Person{}) 可以取得 Person 类型的信息v.Convert 可以将 v 转换为 reflect.TypeOf(Person{}) 指定的类型 没有传递指针给 reflect.ValueOf 如果我们想通过反射对象来修改原变量就必须传递一个指针否则会报错暂不考虑 slice, map, 结构体字段包含指针字段的特殊情况 func TestReflect(t *testing.T) {p : Person{Name: foo}v : reflect.ValueOf(p)// 报错panic: reflect: reflect.Value.SetString using unaddressable valuev.FieldByName(Name).SetString(bar) }这个错误的原因是v 是一个 Person 类型的值而不是指针所以我们不能通过 v.FieldByName(Name) 来修改它的字段。 对于反射对象来说只拿到了 p 的拷贝而不是 p 本身所以我们不能通过反射对象来修改 p。 在一个无效的 Value 上操作 我们有很多方法可以创建 reflect.Value而且这类方法没有 error 返回值这就意味着就算我们创建 reflect.Value 的时候传递了一个无效的值也不会报错而是会返回一个无效的 reflect.Value func TestReflect(t *testing.T) {var p Person{}v : reflect.ValueOf(p)// Person 不存在 foo 方法// FieldByName 返回一个表示 Field 的反射对象 reflect.Valuev1 : v.FieldByName(foo)assert.False(t, v1.IsValid())// v1 是无效的只有 String 方法可以调用// 其他方法调用都会 panicassert.Panics(t, func() {// panic: reflect: call of reflect.Value.NumMethod on zero Valuefmt.Println(v1.NumMethod())}) }对于这个问题我们可以通过 IsValid 方法来判断 reflect.Value 是否有效 func TestReflect(t *testing.T) {var p Person{}v : reflect.ValueOf(p)v1 : v.FieldByName(foo)// 通过 IsValid 判断 reflect.Value 是否有效if v1.IsValid() {fmt.Println(p has foo field)} else {fmt.Println(p has no foo field)} }Field() 方法在传递的索引超出范围的时候直接 panic而不会返回一个 invalid 的 reflect.Value。 IsValid 报告反射对象 v 是否代表一个值。 如果 v 是零值则返回 false。 如果 IsValid 返回 false则除 String 之外的所有其他方法都将发生 panic。 大多数函数和方法从不返回无效值。 什么时候 IsValid 返回 false reflect.Value 的 IsValid 的返回值表示 reflect.Value 是否有效而不是它代表的值是否有效。比如 var b *int nil v : reflect.ValueOf(b) fmt.Println(v.IsValid()) // true fmt.Println(v.Elem().IsValid()) // false fmt.Println(reflect.Indirect(v).IsValid()) // false在上面这个例子中v 是有效的它表示了一个指针指针指向的对象为 nil。 但是 v.Elem() 和 reflect.Indirect(v) 都是无效的因为它们表示的是指针指向的对象而指针指向的对象为 nil。 我们无法基于 nil 来做任何反射操作。 其他情况下 IsValid 返回 false 除了上面的情况IsValid 还有其他情况下会返回 false 空的反射值对象获取通过 nil 创建的反射对象其 IsValid 会返回 false。结构体反射对象通过 FieldByName 获取了一个不存在的字段其 IsValid 会返回 false。结构体反射对象通过 MethodByName 获取了一个不存在的方法其 IsValid 会返回 false。map 反射对象通过 MapIndex 获取了一个不存在的 key其 IsValid 会返回 false。 示例 func TestReflect(t *testing.T) {// 空的反射对象fmt.Println(reflect.Value{}.IsValid()) // false// 基于 nil 创建的反射对象fmt.Println(reflect.ValueOf(nil).IsValid()) // falses : struct{}{}// 获取不存在的字段fmt.Println(reflect.ValueOf(s).FieldByName().IsValid()) // false// 获取不存在的方法fmt.Println(reflect.ValueOf(s).MethodByName().IsValid()) // falsem : map[int]int{}// 获取 map 的不存在的 keyfmt.Println(reflect.ValueOf(m).MapIndex(reflect.ValueOf(3)).IsValid()) }注意还有其他一些情况也会使 IsValid 返回 false这里只是列出了部分情况。 我们在使用的时候需要注意我们正在使用的反射对象会不会是无效的。 通过反射修改不可修改的值 对于 reflect.Value 对象我们可以通过 CanSet 方法来判断它是否可以被设置 func TestReflect(t *testing.T) {p : Person{Name: foo}// 传递值来创建的发射对象// 不能修改其值因为它是一个副本v : reflect.ValueOf(p)assert.False(t, v.CanSet())assert.False(t, v.Field(0).CanSet())// 下面这一行代码会 panic// panic: reflect: reflect.Value.SetString using unaddressable value// v.Field(0).SetString(bar)// 指针反射对象本身不能修改// 其指向的对象也就是 v1.Elem()可以修改v1 : reflect.ValueOf(p)assert.False(t, v1.CanSet())assert.True(t, v1.Elem().CanSet()) }CanSet 报告 v 的值是否可以更改。只有可寻址addressable且不是通过使用未导出的结构字段获得的值才能更改。 如果 CanSet 返回 false调用 Set 或任何类型特定的 setter例如 SetBool、SetInt将 panic。CanSet 的条件是可寻址。 对于传值创建的反射对象我们无法通过反射对象来修改原变量CanSet 方法返回 false。 例外的情况是如果这个值中包含了指针我们依然可以通过那个指针来修改其指向的对象。 只有通过 Elem 方法的返回值才能设置指针指向的对象。 在错误的 Value 上调用 Elem 方法 reflect.Value 的 Elem() 返回 interface 的反射对象包含的值或指针反射对象指向的值。如果反射对象的 Kind 不是 reflect.Interface 或 reflect.Pointer它会发生 panic。 如果反射对象为 nil则返回零值。 我们知道interface 类型实际上包含了类型和数据。而我们传递给 reflect.ValueOf 的参数就是 interface所以在反射对象中也提供了方法来获取 interface 类型的类型和数据 func TestReflect(t *testing.T) {p : Person{Name: foo}v : reflect.ValueOf(p)// 下面这一行会报错// panic: reflect: call of reflect.Value.Elem on struct Value// v.Elem()fmt.Println(v.Type())// v1 是 *Person 类型的反射对象是一个指针v1 : reflect.ValueOf(p)fmt.Println(v1.Elem(), v1.Type()) }在上面的例子中v 是一个 Person 类型的反射对象它不是一个指针所以我们不能通过 v.Elem() 来获取它指向的对象。 而 v1 是一个指针所以我们可以通过 v1.Elem() 来获取它指向的对象。 调用了一个其类型不能调用的方法 这可能是最常见的一类错误了因为在 go 的反射系统中我们调用的一些方法又会返回一个相同类型的反射对象但是这个新的反射对象可能是一个不同的类型了。同时返回的这个反射对象是否有效也是未知的。 在 go 中反射有两大对象 reflect.Type 和 reflect.Value它们都存在一些方法只适用于某些特定的类型也就是说 在 go 的反射设计中只分为了类型和值两大类。但是实际的 go 中的类型就有很多种比如 int、string、struct、interface、slice、map、chan、func 等等。 我们先不说 reflect.Type我们从 reflect.Value 的角度看看将这么多类型的值都抽象为 reflect.Value 之后 我们如何获取某些类型值特定的信息呢比如获取结构体的某一个字段的值或者调用某一个方法。 这个问题很好解决需要获取结构体字段是吧那给你提供一个 Field() 方法需要调用方法吧那给你提供一个 Call() 方法。 但是这样一来有另外一个问题就是如果我们的 reflect.Value 是从一个 int 类型的值创建的 那么我们调用 Field() 方法就会发生 panic因为 int 类型的值是没有 Field() 方法的 func TestReflect(t *testing.T) {p : Person{Name: foo}v : reflect.ValueOf(p)// 获取反射对象的 Name 字段assert.Equal(t, foo, v.Field(0).String())var i 1v1 : reflect.ValueOf(i)assert.Panics(t, func() {// 下面这一行会 panic// v1 没有 Field 方法fmt.Println(v1.Field(0).String())}) }至于有哪些方法是某些类型特定的可以参考一下我的另一篇文章《深入理解 go reflect - 反射基本原理》 总结 在调用 Int()、Float() 等方法时需要确保反射对象的类型是正确的类型否则会 panic比如在一个 flaot 类型的反射对象上调用 Int() 方法就会 panic。如果想修改原始的变量创建 reflect.Value 时需要传入原始变量的指针。如果 reflect.Value 的 IsValid() 方法返回 false那么它就是一个无效的反射对象调用它的任何方法都会 panic除了 String 方法。对于基于值创建的 reflect.Value如果想要修改它的值我们无法调用这个反射对象的 Set* 方法因为修改一个变量的拷贝没有任何意义。同时我们也无法通过 reflect.Value 去修改结构体中未导出的字段即使我们创建 reflect.Value 时传入的是结构体的指针。Elem() 只可以在指针或者 interface 类型的反射对象上调用否则会 panic它的作用是获取指针指向的对象的反射对象又或者获取接口 data 的反射对象。reflect.Value 和 reflect.Type 都有很多类型特定的方法比如 Field()、Call() 等这些方法只能在某些类型的反射对象上调用否则会 panic。
http://www.pierceye.com/news/19913/

相关文章:

  • wordpress能做手机站吗90设计网官网 登录
  • 中国智慧团建网站企业官网网站
  • 人才网网站建设方案联想服务器怎么建设第二个网站
  • 网站响应式是什么意思如何实现一个响应式网页
  • 朵朵软件网站建设东莞市网络公司
  • 洛阳网站公司哪家好服装设计网上自学课程
  • 贵阳网站如何推广wordpress 微官网主题下载失败
  • 微网站 微官网的区别登封做网站推广
  • 四川电脑网站建设网站建设管理工作总结
  • 网站制作企业首页香蜜湖附近网站建设
  • 网站如何推广出去wordpress 故障宕机
  • 做网站公司怎么样wordpress当前分类下所有子分类
  • 北京商务网站建设建设工程项目报建网站
  • 网站系统模板wordpress保存文件
  • 手机备案网站外包网站都有哪些
  • 网站建设流程wordpress 读取文章
  • 江门网站建设方案wordpress获取小工具
  • 网站建设包含项目青岛即墨网站网页设计
  • 网盘网站开发百度关键词代做排名
  • 音乐网站要怎么做浏览器老是出现站长工具
  • 网站的组成部分WordPress禁用代码编辑器
  • 网站设计的基本原则长沙网站优化外包服务
  • 网站构建免费上海建站提供商
  • 网站做接口手机兼职赚钱正规平台
  • 网站建设材料河南城市建设招标类网站
  • 全景图制作平台网站建设9377传奇手游官网
  • 宁波网站建设公司排名seo课程培训视频
  • 在淘宝上的毕设网站代做网站开发培训好学吗
  • 途途外贸企业网站管理系统阅读网站模板
  • 免费网站软件下载专业网站建设组织