设为首页 收藏本站
查看: 1166|回复: 0

[经验分享] GOLANG 反射法则

[复制链接]

尚未签到

发表于 2018-9-19 13:02:01 | 显示全部楼层 |阅读模式
  译自[blog.golang.org/laws-of-reflection]
  在计算机中, 反射是程序通过类型,检测到它自己的结构能力;是一种元编程程;也是一个具大的混淆点
  在本文中,我们将通过解释反射是如何在GO中工作的来澄清它。每个语言的反射模式是不同的。本文着重于GO,
  所以后文中的反射都是指GO中的反射
  1.类型和接口
  因为反射是修建于类型系统之上, 所以让我们从GO的类型开始讲吧。
  GO是静态类型语言。 每个变量都有一个静态类型。 也就是说, 每一个已经类型在编译时已经固定了其类型:int, float32...等
  假如我们声明如下:
  type MyInt int
  var i int
  var j MyInt
  则i的类型为int; j的类型为MyInt;变量i和j有明显不同的静态类型, 而潜在下层类型, 他们可以彼此赋值不须要转换。
  还有一种重要类型是接口类型,它代表了一组固定的方法集。一个接口变量能存储任一实体值,只要它实现了接口方法集。以大家所
  熟知的接口为例, io.Reader和io.Writer, 它们的类型摘自io 包。
  // Reader 就是一个包含了基本读方法的接口
  type Reader interface {
  Read(p []byte[(n int, err error)
  }
  // Writher就是一个包含了基本写方法的接口
  type Writer interface{
  Write(p []byte)(n int, err error);
  }
  任何实现了上述读(写)方法签名的类型就说它实现了io.Reader(或者io.Writer).讨论这个的目标是, 就是指一个io.Reader类型的变量
  可以带有任何值,只要它的类型中有一个Read方法
  var r io.Reader
  r = os.Stdin
  r = bufio.NewReader(r)
  r = new (bytes.Buffer)
  // 等等 ....都可以的...
  无论r带有什么实例值,r的类型总是io.Reader,明白这点是非常重要的;GO是静态类型,r的静态类型就是io.Reader。
  另一个极其重要的接口例子就是空接口
  interface{}
  它代表一个空方法集。它可以被任何值满足。因为任何值都有0到多个方法
  有些朋友说,GO的接口是动态类型的, 这就是错误引导。 他们是确实静态类型:一个接口类型的变量总是有一个相同的静态类型,
  虽然,在运行时,一个存储在一个接口变量上的值可能会改变类型,但值总是满足接口。
  我们需要准确的了解这些,因为反射和接口太相似了。
  2. 接口的表达
  Russ Cox写了一篇博客关于GO语言接口值的表达【research.swtch.com/interface】, 我就没有必须重复这个故事了。简单总结如下
  接口类型的变量存成一个pair(对):值赋给变量和它的描述符descriptor;更准确的说,就是潜在下层的实例值实现了接口,它的类型
  描述了下层实例值的完整类型。例如:
  var r io.Reader
  tty , err := os.OpenFile("/dev/tty",os.O_RDWR, 0)
  if er != nil{
  return nil, err
  }
  r = tty
  r包含了(值value,类型type)pair对,(tty,*os.File). 注意,*os.File类型实现了远不只Read方法;虽然接口值提供只有Read方法。
  内部值包含所有关于值的类型信息,这就是为什么我们可以如下处理
  var w io.Writer
  w = r.(io.Writer) // 类型断言转换
  这在个赋值表达式中是一个类型断言;它判言的是r里面的东西也实现了io.writer.并且我们能赋值给w, 赋值后,w包含(tty,*os.File)对。
  和之前r所包含的对是一样的的。接口的静态类型确定了通过接口变量那些方法可以被调用。虽然内部实例值可能有一个更大的方法集。
  接着,我们也可以
  var empty interface{}
  empty = w
  我们的空接口值empty,再一次包含了一样的(tty,*os.File)对。这是很方便的, 空接口可以含有什么信息
  一个重要的细节就是, 内部pair对总是有一个(值value,实类型concrete type)并且没有(value, 接口类型interface type)
  现在我们可以谈反射了
  反射法则
  1、反射-从接口值到反射对象
  从基本上讲, 反射仅是一种用来检测存储在接口变量内部(值value,实例类型concret type)pair对的一种机
  制。 开始,在反射包中,有两种类型我们需要了解类型和值。 这两种类型让我们可以访问接口变量内容。
  两个简单函数分别为reflect.TypeOf和reflect.Valueo,它们从接口变量中取出reflect.Type和
  reflect.Value,(也可以从reflect.Value轻松获得reflect.Type, 但是现在我们还是将Value和Type概念分
  开来将)
  让我们首先来讲TypeOf
  package main
  import(
  "fmt"
  "reflect"
  )
  func main(){
  var x float64 = 3.4
  fmt.Println("type:", reflect.TypeOf(x);
  }
  程序输出
  type:float64
  你可能会想这里的接口变量在哪里呀,因为程序看来像是传递的一个float64变量x, 不是接口值, 对于
  reflect.TypeOf,包含了一空接口:
  func TypeOf(i inteface{}) Type
  当我们调用reflect.TypeOf(x),首先x被存在一个空接口中,它被当前该接口变量传入到reflect.TypeOf中;
  reflect.TypeOf解包将空接口转为类型信息
  reflect.ValueOf函数,找回值(从这里,我们省略代码模板,聚焦到执行代码上)
  var x float64 = 3.4
  fmt.Println("value:", reflect.ValueOf(x))
  输出
  value:
  reflect.Type和reflect.Value都有方法配槽来让我们来检查与操作它们。 一个重要的例子如,Value有一个
  Type方法返回reflect.Value的类型Type。另一个就是Type和Value都有一个Kind方法返回一个常量表明它是
  哪种存储类型Uint,Float64,Slice等等。 此外,在Value上的有方法名如Int和Float让我们取出里面的值
  var x float64 = 3.4
  v := reflect.ValueOf(x)
  fmt.Println("type:", v.Type())
  fmt.Println("kind is float64:",v.Kind() == reflect.Float64)
  fmt.Println("value:",v.Float)
  输出:
  type:float64
  kind is float64:true
  value: 3.4
  还有方法如SetInt和SetFloat,但是使用它们要理解他们的设置能力,反射的第三条法则,讨论如下:
  反射库有一些属性值得列出来,首先,保持API简单, 值的getter和setter操作在它可以操作的最大的类型
  上:对于所有有符号数为Int64; 例如,值的Int方法返回一个int64,SetInt需要一个int64; 它可能需要转换
  成实际值
  var x uint8 = 'x'
  v := reflect.ValueOf(x)
  fmt.Println("type:",v.Type())         // uint8
  fmt.Println("kind is uint8:", v.Kind() == reflect.Uint8) // true
  x = uint8(v.Uint())          // v.Uint返回uint64
  第二个属性就是,反射对象的Kind描述了潜在下层的类型Type, 并不是静态类型。 假如一个反射对象包含一个用户定义整形类型的值,如下:
  type MyInt int
  var x MyInt = 7
  v := reflect.ValueOf(x)
  v的Kind仍然是reflect.Int, 虽然x的静态类型是MyInt,而不是int, 换名话说,Kind不区分int和MyInt,虽然Type能。
  反射第二法则
  2.反射-从反射对象到接口值
  就像物理反射一样,反射在GO生成自己的反像。
  给定一个reflect.Value我们使用Interface方法能找回一个接口值;在effect方法打包了类型和值信息到一个接口中并返回结果。
  // 返回v的值作为一个接口变量
  func (v Value)Interface() interface{}
  由此我们可以说
  y := v.Inteface().(float64) // y 有类型float64
  fmt.Println(y)
  。。。。。。。待续


运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-594288-1-1.html 上篇帖子: golang GUI 下篇帖子: 开始编写Golang代码
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表