1.反射:

定义: 反射就是程序能够在运行时检查变量和值,求出它们的类型。
                   可以在运行时动态获取变量的相关信息
                   Import ("reflect")

为什么需要反射?
       想象下:如果程序中每个变量都是我们自己定义的,那么在编译时就可以知道变量类型了,但是实际中并非如此,就需要我们在运行时检查变量,求出它的类型。这就需要用到反射。

在 Go 语言中,reflect 实现了运行时反射。reflect 包会帮助识别 interface{} 变量的底层具体类型和具体值。

几个函数:
a. reflect.TypeOf(val),获取变量的类型,返回reflect.Type类型
b. reflect.ValueOf(val),获取变量的值,返回reflect.Value类型
c. reflect.Value.Kind(),获取变量的类别,返回一个常量
d. reflect.Value.Interface(),转换成interface{}类型

变量接口及获取变量值之间的转换:

 package main

 import (
"fmt"
"reflect"
) type Student struct {
Name string
Age int
Score float32
} func test(b interface{}) {
t := reflect.TypeOf(b)
fmt.Println("t: ", t) //t: main.Student v := reflect.ValueOf(b)
fmt.Println("v: ", v) //v: {stu01 18 92} k := v.Kind()
fmt.Println("k: ", k) //k: struct iv := v.Interface()
fmt.Println("iv: ", iv) //iv: {stu01 18 92}
stu, ok := iv.(Student)
if ok {
fmt.Printf("%v %T\n", stu, stu) //{stu01 18 92} main.Student
}
} func main() {
var a Student = Student{
Name: "stu01",
Age: ,
Score: ,
}
test(a)
}

example

 package main

 import (
"fmt"
"reflect"
) func main() { var x float64 = 3.4
fmt.Println("type:", reflect.TypeOf(x)) //type: float64
v := reflect.ValueOf(x)
fmt.Printf("value:%v, type:%T\n", v, v) //value:3.4, type: reflect.Valuetype
fmt.Println("type:", v.Type()) //type: float64
fmt.Println("kind:", v.Kind()) //kind: float64
fmt.Println("value:", v.Float()) //value: 3.4 fmt.Println(v.Interface()) //3.4
fmt.Printf("value is %5.2e\n", v.Interface()) //value is 3.40e+00
y := v.Interface().(float64) //v -> Interface -> float64 -> y
fmt.Println(y) //3.4
}

example2

2. reflect.Value.Kind()方法返回的常量

 const (
Invalid Kind = iota
Bool
Int
Int8
Int16
Int32
Int64
Uint
Uint8
Uint16
Uint32
Uint64
Uintptr
Float32
Float64
Complex64
Complex128
Array
Chan
Func
Interface
Map
Ptr
Slice
String
Struct
UnsafePointer
)

Kind返回的常量

3. 获取变量的值:

reflect.ValueOf(x).Float()
reflect.ValueOf(x).Int()
reflect.ValueOf(x).String()
reflect.ValueOf(x).Bool()

4. 通过反射的来改变变量的值
    reflect.Value.SetXX相关方法,比如:

reflect.Value.SetFloat(),设置浮点数
reflect.Value.SetInt(),设置整数
reflect.Value.SetString(),设置字符串

练习:(panic: reflect: reflect.Value.SetFloat using unaddressable value)

 package main

 import (
"fmt"
"reflect"
) func main() {
var a float64
fv := reflect.ValueOf(a)
fv.SetFloat(3.3)
fmt.Printf("%v\n", a)
}

程序崩溃了

崩溃的原因:还是值类型和引用类型的原因。v := reflect.ValueOf(x) ,v是x的一个拷贝,修改v,x不会修改!

解决方法:传地址!

 package main

 import (
"fmt"
"reflect"
) func main() {
var a float64 = 1.0 fv := reflect.ValueOf(&a)
fv.Elem().SetFloat(3.3) //相当于var *fv float64; *fv = 3.3
fmt.Printf("%v\n", a) //3.3
}

传地址

其中fv.Elem().Setxx用来获取指针指向的变量,相当于:
       var a *int;
       *a = 100

5. 用反射操作结构体
    a. reflect.Value.NumField() 获取结构体中字段的个数
    b. reflect.Value.Method(n).Call 来调用结构体中的方法

 func (Value) Call
func (v Value) Call(in []Value) []Value
Call calls the function v with the input arguments in. For example, if len(in) == , v.Call(in) represents the Go call v(in[], in[], in[]). Call panics if v's Kind is not Func. It returns the output results as Values. As in Go, each input argument must be assignable to the type of the function's corresponding input parameter. If v is a variadic function, Call creates the variadic slice parameter itself, copying in the corresponding values. func (Value) Elem
func (v Value) Elem() Value
Elem returns the value that the interface v contains or that the pointer v points to. It panics if v's Kind is not Interface or Ptr. It returns the zero Value if v is nil. func (Value) NumField
func (v Value) NumField() int
NumField returns the number of fields in the struct v. It panics if v's Kind is not Struct.

Elem, NumField,Call官方解释

 package main

 import (
"encoding/json"
"fmt"
"reflect"
) type Student struct {
Name string `json:"student_name"`
Age int
Score float32
Sex string
} func (s Student) Print() {
fmt.Println("---start----")
fmt.Println(s)
fmt.Println("---end----")
} func (s Student) Set(name string, age int, score float32, sex string) {
s.Name = name
s.Age = age
s.Score = score
s.Sex = sex
fmt.Println("set -----------")
} func TestStruct(a interface{}) {
tye := reflect.TypeOf(a)
val := reflect.ValueOf(a)
kd := val.Kind()
// fmt.Println(tye, val, kd, val.Elem().Kind()) //*main.Student &{stu01 18 92.8 } ptr struct if kd != reflect.Ptr && val.Elem().Kind() == reflect.Struct {
fmt.Println("expect struct")
return
} num := val.Elem().NumField() //获取val
val.Elem().Field().SetString("stu1000")
for i := ; i < num; i++ {
fmt.Printf("%d %v\n", i, val.Elem().Field(i).Kind())
} fmt.Printf("struct has %d fields\n", num) tag := tye.Elem().Field().Tag.Get("json")
fmt.Printf("tag=%s\n", tag) //tag=student_name numOfMethod := val.Elem().NumMethod() //
fmt.Printf("struct has %d methods\n", numOfMethod)
var params []reflect.Value
val.Elem().Method().Call(params)
} func main() {
var a Student = Student{
Name: "stu01",
Age: ,
Score: 92.8,
} result, _ := json.Marshal(a)
fmt.Println("json result:", string(result)) TestStruct(&a)
fmt.Println(a)
}

example

6. 反射中调用函数

Go中的函数是可以像普通的 int、float 等类型变量那样作为值的。例如:

 package main

 import "fmt"

 func Print() {
fmt.Println("Hello world!")
} func main() {
f := Print
f()
}

函数作为变量

和函数作为变量类似,在反射中函数和方法的类型(Type)都是 reflect.Func,如果要调用函数的话,可以通过 Value的Call()方法。

 package main

 import (
"fmt"
"reflect"
) func Print() {
fmt.Println("Hello world!")
} func main() {
f := Print
fv := reflect.ValueOf(f)
fmt.Println("fv is reflect.Func? ", fv.Kind() == reflect.Func) //fv is reflect.Func? true
fv.Call(nil) //Hello world!
}

测试reflect.Func

Call函数的定义:func (v Value) Call(in []Value) []Value
Value 的 Call() 方法的参数是一个 Value 的 slice,对应的反射函数类型的参数,返回值也是一个 Value 的 slice,同样对应反射函数类型的返回值。

 package main

 import (
"fmt"
"reflect"
) func Print(str string) string {
str = fmt.Sprintf("hello %s", str)
return str
} func main() {
fv := reflect.ValueOf(Print)
params := make([]reflect.Value, ) // 传给Print的参数
params[] = reflect.ValueOf("zhangsan") // 参数设置为"zhangsan"
rs := fv.Call(params) // rs作为结果接受函数的返回值
//result: hello zhangsan
fmt.Println("result:", rs[].Interface().(string)) // rs[0].Interface() ok
}

example

7. 反射中调用方法

函数和方法可以说其实本质上是相同的,只不过方法与一个“对象”进行了“绑定”,方法是“对象”的一种行为,这种行为是对于这个“对象”的一系列操作,例如修改“对象”的某个属性。

 package main

 import (
"fmt"
"reflect"
) type Student struct {
Name string
Age int
} func (s *Student) SetName(name string) {
s.Name = name
} func (s *Student) SetAge(age int) {
s.Age = age
} func (s *Student) Print() string {
str := fmt.Sprintf("%s is %d years old.", s.Name, s.Age)
return str
} func main() {
stu := &Student {
Name:"zhangsan",
Age:,
} ref := reflect.ValueOf(stu)
fmt.Println("Before:", ref.MethodByName("Print").Call(nil)[]) //Before: zhangsan is 20 years old. params := make([]reflect.Value, ) params[] = reflect.ValueOf("lisi")
ref.MethodByName("SetName").Call(params)
fmt.Println(stu) //&{lisi 20} params[] = reflect.ValueOf()
ref.MethodByName("SetAge").Call(params) fmt.Println(stu) //&{lisi 22}
fmt.Println("After:", ref.MethodByName("Print").Call(nil)[]) //After: lisi is 22 years old.
}

example

练习1:

 package main

 import (
"fmt"
"reflect"
) type NotknownType struct {
s1 string
s2 string
s3 string
} func (n NotknownType) String() string {
return n.s1 + "-" + n.s2 + "-" + n.s3
} var secret interface{} = NotknownType{"Ada", "Go", "Oberon"} func main() {
value := reflect.ValueOf(secret)
typ := reflect.TypeOf(secret)
fmt.Println(typ) // Main.NotknownType knd := value.Kind()
fmt.Println(knd) // struct for i := ; i < value.NumField(); i++ {
fmt.Printf("Field %d: %v\n", i, value.Field(i))
//value.Field(i).SetString("C#")
} results := value.Method().Call(nil)
fmt.Println(results) // [Ada - Go - Oberon]
}

练习

练习2:通过反射操作结构体

 package main

 import (
"fmt"
"reflect"
) type NotknownType struct {
s1 string
s2 string
s3 string
} func (n NotknownType) String() string {
return n.s1 + "-" + n.s2 + "-" + n.s3
} var secret interface{} = NotknownType{"Ada", "Go", "Oberon"} func main() {
value := reflect.ValueOf(secret)
typ := reflect.TypeOf(secret)
fmt.Println(typ) // Main.NotknownType knd := value.Kind()
fmt.Println(knd) // struct for i := ; i < value.NumField(); i++ {
fmt.Printf("Field %d: %v\n", i, value.Field(i))
//value.Field(i).SetString("C#")
} results := value.Method().Call(nil)
fmt.Println(results) // [Ada - Go - Oberon]
}

example

练习3:通过反射修改结构体

 package main

 import (
"fmt"
"reflect"
) type T struct {
A int
B string
} func main() {
t := T{, "skidoo"}
s := reflect.ValueOf(&t).Elem()
typeOfT := s.Type() //main.T
for i := ; i < s.NumField(); i++ {
f := s.Field(i)
fmt.Printf("%d: %s %s = %v\n", i,
typeOfT.Field(i).Name, f.Type(), f.Interface())
}
s.Field().SetInt()
s.Field().SetString("Sunset Strip")
fmt.Println("t is now", t) //t is now {77 Sunset Strip}
}

example

建议:反射是 Go 语言中非常强大和高级的概念,我们应该小心谨慎地使用它。使用反射编写清晰和可维护的代码是十分困难的。你应该尽可能避免使用它,只在必须用到它时,才使用反射。

图书管理系统v2版本开发:

实现一个图书管理系统v2,具有以下功能:
     a. 增加用户登录、注册功能
     b. 增加借书过期的图书界面
     c. 增加显示热门图书的功能,被借次数最多的top10
     d. 增加查看某个人的借书记录的功能

参考文献:

  • https://studygolang.com/articles/13178 (Go 系列教程 - 反射)
  • https://golang.org/pkg/reflect/
  • https://www.cnblogs.com/52php/p/6337420.html

Go语言学习之6 反射详解的更多相关文章

  1. Go语言学习之8 goroutine详解、定时器与单元测试

    主要内容: 1.Goroutine2. Chanel3. 单元测试 1. Goroutine Go 协程(Goroutine)(轻量级的线程,开线程没有数量限制).   (1)进程和线程 A. 进程是 ...

  2. [深入学习Web安全](5)详解MySQL注射

    [深入学习Web安全](5)详解MySQL注射 0x00 目录 0x00 目录 0x01 MySQL注射的简单介绍 0x02 对于information_schema库的研究 0x03 注射第一步—— ...

  3. Shell学习之Bash变量详解(二)

    Shell学习之Bash变量详解 目录 Bash变量 Bash变量注意点 用户自定义变量 环境变量 位置参数变量 预定义变量 Bash变量 用户自定义变量:在Bash中由用户定义的变量. 环境变量:这 ...

  4. Asp.Net MVC学习总结之过滤器详解(转载)

    来源:http://www.php.cn/csharp-article-359736.html   一.过滤器简介 1.1.理解什么是过滤器 1.过滤器(Filters)就是向请求处理管道中注入额外的 ...

  5. C#反射の反射详解

    C#反射の反射详解(点击跳转)C#反射の反射接口(点击跳转)C#反射反射泛型接口(点击跳转)C#反射の一个泛型反射实现的网络请求框架(点击跳转) 一.什么是反射 反射(Reflection):这是.N ...

  6. Linux学习之用户配置文件详解(十四)

    Linux学习之用户配置文件详解 目录 用户信息文件/etc/password 影子文件/etc/shadow 组信息文件/etc/group 组密码文件/etc/gshadow 用户信息文件/etc ...

  7. [转载]springmvc学习之@ModelAttribute运用详解

    spring学习之@ModelAttribute运用详解 链接

  8. expect学习笔记及实例详解【转】

    1. expect是基于tcl演变而来的,所以很多语法和tcl类似,基本的语法如下所示:1.1 首行加上/usr/bin/expect1.2 spawn: 后面加上需要执行的shell命令,比如说sp ...

  9. Android 布局学习之——Layout(布局)详解二(常见布局和布局参数)

    [Android布局学习系列]   1.Android 布局学习之——Layout(布局)详解一   2.Android 布局学习之——Layout(布局)详解二(常见布局和布局参数)   3.And ...

随机推荐

  1. mysql导入导出表

    导入 source ***(路径+文件) 导出 mysqldump -uroot -plizhenghua 数据库名 表名 > 你要保存的sql文件(加位置)

  2. CIL锁,GIL与线程池的区别,进程池和线程池,同步与异步

    一.GIL锁 什么是GIL? 全局解释器锁,是加在解释器上的互斥锁 GC是python自带的内存管理机制,GC的工作原理:python中的内存管理使用的是应用计数,每个数会被加上一个整型的计数器,表示 ...

  3. 【python51--__name__属性】

    一.基础知识 1.__name__ == '__main__' 所有模块都有一个__name__属性,__name__的值取决于如何应用模块,在作为独立程序运行的时候,__name__属性的值是‘__ ...

  4. CocoaPods出错

    1 Error: pod search Masonry /usr/local/lib/ruby/gems/2.3.0/gems/cocoapods-1.4.0.beta.2/lib/cocoapods ...

  5. jsoup对 HTML 文档的解析和操作

    本文手动转载自http://www.cnblogs.com/chenying99/archive/2013/01/04/2844615.html,仅根据个人需要对实用部分进行转载,详细请阅读原文. j ...

  6. topcoder srm 691 div1 -3

    1.给定一个$n$个顶点$n$个边的图,边是$(i,a_{i})$,顶点编号$[0,n-1]$.增加一个顶点$n$,现在选出一个顶点集$M$,对于任意的在$M$中 的顶点$x$,去掉边$(x,a_{x ...

  7. 数论+矩阵快速幂|斐波那契|2014年蓝桥杯A组9-fishers

    标题:斐波那契 斐波那契数列大家都非常熟悉.它的定义是: f(x) = 1 .... (x=1,2) f(x) = f(x-1) + f(x-2) .... (x>2) 对于给定的整数 n 和 ...

  8. dp专题练习

    顺便开另外一篇放一些学过的各种dp dp总结:https://www.cnblogs.com/henry-1202/p/9194066.html 开坑先放15道题,后面慢慢补 目标50道题啦~~,目前 ...

  9. Sample Classification Code of CIFAR-10 in Torch

    Sample Classification Code of CIFAR-10 in Torch from: http://torch.ch/blog/2015/07/30/cifar.html req ...

  10. [午间休息] - 午间codewars活跃脑袋

    https://www.codewars.com/kata/51f2b4448cadf20ed0000386/javascript 中午是一个易困的时间段.如果其它人不睡觉还好. 这个js题目就是说如 ...