写点什么

Go 学习笔记之 反射

发布于: 1 小时前
Go 学习笔记之 反射

反射,Go 语言提供的一种机制,能够在运行时更新变量和检查它们的值、调用它们的方法和它们支持的内在操作,而不需要在编译时就知道这些变量的具体类型。


为什么需要反射?

有时候我们需要编写一个函数能够处理一类并不满足普通公共接口的类型的值,也可能是因为它们并没有确定的表示方式,或者是在我们设计该函数的时候这些类型可能还不存在。


一个大家熟悉的例子是 fmt.Fprintf 函数提供的字符串格式化处理逻辑,它可以用来对任意类型的值格式化并打印,甚至支持用户自定义的类型。让我们也来尝试实现一个类似功能的函数。

为了简单起见,我们的函数只接收一个参数,然后返回和 fmt.Sprint 类似的格式化后的字符串。

我们实现的函数名也叫 Sprint。


func Sprint(x interface{}) string {    type stringer interface {        String() string    }    switch x := x.(type) {    case stringer:        return x.String()    case string:        return x    case int:        return strconv.Itoa(x)    // ...similar cases for int16, uint32, and so on...    case bool:        if x {            return "true"        }        return "false"    default:        // array, chan, func, map, pointer, slice, struct        return "???"    }}
复制代码

首先用 switch 类型分支来测试输入参数是否实现了 String 方法,如果是的话就调用该方法。然后继续增加类型测试分支,检查这个值的动态类型是否是 string、int、bool 等基础类型,并在每种情况下执行相应的格式化操作。

问题:

但是我们如何处理其它类似[]float64、map[string][]string 等类型呢?我们当然可以添加更多的测试分支,但是这些组合类型的数目基本是无穷的。

没有办法来检查未知类型的表示方式,这时我们就被卡住了。


反射的实现

反射是由 reflect 包提供的。它定义了两个重要的类型,Type 和 Value。一个 Type 表示一个 Go 类型。它是一个接口,有许多方法来区分类型以及检查它们的组成部分,例如一个结构体的成员或一个函数的参数等。唯一能反映 reflect.Type 实现的是接口的类型描述信息(§7.5),也正是这个实体标识了接口值的动态类型。

函数 reflect.TypeOf 接受任意的 interface{} 类型,并以 reflect.Type 形式返回其动态类型:

t := reflect.TypeOf(3)  // a reflect.Typefmt.Println(t.String()) // "int"fmt.Println(t)          // "int"
复制代码


发布于: 1 小时前阅读数: 6
用户头像

坚持分享接地气儿的架构技术文章! 2018.02.26 加入

同名微信公众号「架构精进之路」,专注软件架构研究,技术学习与职业成长!坚持原创总结、沉淀和分享,希望能带给大家一些引导和启发,感谢各位的支持(关注、点赞、分享)!

评论

发布
暂无评论
Go 学习笔记之 反射