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. Android之电话拨号和短信

    拨打电话号码,需要添加权限设置,在安装的应用程序信息中可以看到此权限信息Intent doSth=new Intent();//意图:你想做什么呢?doSth.setAction(Intent.ACT ...

  2. phpstorm 配置点右上角浏览器图标时用浏览器打开当前页面

    文件>设置 只要这一步就可以这样了

  3. 简单的MVC与SQL Server Express LocalDB

    M模式: 类,表示数据的应用程序和使用验证逻辑以强制实施针对这些数据的业务规则. V视图: 应用程序使用动态生成 HTML 响应的模板文件. C控制器: 处理传入的浏览器请求的类中检索模型数据,然后指 ...

  4. 一、数据库表中字段的增删改查,二、路由基础.三、有名无名分组.四、多app共存的路由分配.五、多app共存时模板冲突问题.六、创建app流程.七、路由分发.八、路由别名,九、名称空间.十、反向解析.十一、2.x新特性.十二、自定义转换器

    一.数据库表中字段的增删改查 ''' 直接在modules中对字段进行增删改查 然后在tools下点击Run manage.py Task执行makemigrations和migrate 注意在执行字 ...

  5. NetworkExtension

    一, 按照网上的方法: iOS 无法获取 WiFi 列表?一定是因为你不知道这个框架 提交了申请.两个小时候后苹果回应邮件.意思就是如果只是使用 App Proxy, Content Filter, ...

  6. 对应web的常用flutter应用

    例如,创建一个Text widget: new Text('Hello World', style: new TextStyle(fontSize: 32.0)) 创建一个 Image widget: ...

  7. Python 数据分析 - 索引和选择数据

    loc,iloc,ix三者间的区别和联系 loc .loc is primarily label based, but may also be used with a boolean array. 就 ...

  8. 终于明白vim 和 grep 中 的正则表达式的用法, vim 正则表达式 和grep基本正则表达式 几乎一样

    要搞清楚 vim中的正则和普通的Perl正则表达式的区别: 因为在perl中所有的元字符 都可以直接使用, 不需要在 元字符的前面加 反斜杠. 但是在vim, 包括grep中就有所区别, 同样是元字符 ...

  9. SpringBoot 使用Druid连接池

    1.pom依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

  10. UVA 818 Cutting Chains(状压 + 暴搜)题解

    题意:有1~n个小环,他们中的有些互相扣在一起,问你至少切开几个能把这写小环串成一条链 思路:还是太菜了,题目给的n<=15,显然可以暴力解决. 用二进制表示每个环切还是不切,然后搜索所有情况. ...