文档章节

Go笔记-反射

漂泊尘埃
 漂泊尘埃
发布于 2017/02/28 18:44
字数 1674
阅读 42
收藏 0
点赞 0
评论 0

类型Type

整型

s1 := reflect.TypeOf((int8)(0)).String() // int8

等同于下面这个:

var i8 int8 = 0
reflect.TypeOf(i8).String() // int8

指针

reflect.TypeOf((*int8)(nil)).String() // *int8

等同于:

var i8 *int8 = nil
reflect.TypeOf(i8).String() // *int8

指针指向元素的类型

reflect.TypeOf((*int8)(nil)).Elem().String() // int8

结构体

st := (*struct {
	a int
})(nil)
reflect.TypeOf(st).String() // *struct { a int }
reflect.TypeOf(st).Elem().String() // struct { a int }

结构体字段

	reflect.TypeOf(st).Elem().Field(0).Type.String() // int

	f, found := reflect.TypeOf(st).Elem().FieldByName("a")
	if !found {
		fmt.Println("no field 'a'.")
	} else {
		fmt.Println(f.Type.String()) // int
	}

测试指针和非指针引用的结构体获取字段和方法的能力

func main() {
	var i1 interface{}
	i1 = &T{"t1"}

	var i2 interface{}
	i2 = T{"t2"}

	v1 := reflect.ValueOf(i1)
	v2 := reflect.ValueOf(i2)

	// 字段
	//	fmt.Println(v1.FieldByName("A")) // err: call of reflect.Value.FieldByName on ptr Value
	fmt.Println(v2.FieldByName("A"))

	// 方法
	fmt.Println(v1.MethodByName("Fun1"))
	fmt.Println(v1.MethodByName("Fun2"))

	fmt.Println(v2.MethodByName("Fun1"))
	fmt.Println(v2.MethodByName("Fun2")) // <invalid Value>
	
	if m := v2.MethodByName("Fun2"); m.IsValid() {
		fmt.Println("v2.Fun2() is valid.")
	} else {
		fmt.Println("v2.Fun2() is invalid") // this one
	}
}

type T struct {
	A string
}

func (t T) Fun1() {

}

func (t *T) Fun2() {

}

指针不能获取到字段值。

非指针不能获取到接受者是指针的方法,但反之却可以。

获取到方法后判断一下是不是可用(IsValid())

数组

reflect.TypeOf([32]int{}).String() // [32]int

数组元素类型

reflect.TypeOf([32]int{}).Elem().String() // int

map

maptype := reflect.TypeOf((map[string]*int32)(nil))
maptype.String()        // map[string]*int32
maptype.Key().String()  // string
maptype.Elem().String() // *int32

chan

chantype := reflect.TypeOf((chan<- string)(nil))
chantype.String()        // chan<- string
chantype.Elem().String() // string

接口

获得接口的值的真实类型

var inter struct {
	E interface{}
}
inter.E = 123.456

innerPtrValue := reflect.ValueOf(&inter)
eField := innerPtrValue.Elem().Field(0)
eField.Type() // interface {}

reflect.ValueOf(eField.Interface()).Type() // float64

或者

eField.Elem().Type()

判断interface{}指向的元素的类型

	var i interface{}
	i = "hello"

	t := reflect.ValueOf(i).Type()
	fmt.Println(t) // string

但如果传给i的是个指针的话:

	var i interface{}
	s := "hello"
	i = &s

	t := reflect.ValueOf(i).Type()
	fmt.Println(t) // *string

如果使用reflect.ValueOf(i).Elem().Type()可以获得,但是这个对于不是指针的话就会报错,因为Elem()不能用于string

可以使用reflect.Indirect():

	var i interface{}
	s := "hello"
	i = &s // i = s也是输出string

	v := reflect.ValueOf(i)
	t := reflect.Indirect(v).Type()
	fmt.Println(t) // string

下面是Indirect()的实现:

func Indirect(v Value) Value {
	if v.Kind() != Ptr {
		return v
	}
	return v.Elem()
}

可以参考一下接口为nil时类型和值都必须是nil的介绍。

Kind

reflect.TypeOf((int)(0)).Kind() == reflect.Int
reflect.TypeOf((*int)(nil)).Kind() == reflect.Ptr
reflect.TypeOf((map[string]string)(nil)).Kind() == reflect.Map

反射对象的 Kind 描述了底层类型,而不是静态类型。如果一个反射对象包含了用户定义的整数类型的值,就像:

type MyInt int
var x MyInt = 7
v := reflect.ValueOf(x)

v 的 Kind 仍然是 reflect.Int,尽管 x 的静态类型是 MyInt,而不是 int。换句话说,Kind 无法从 MyInt 中区分 int,而 Type 可以。

获取函数名

func getFuncName(f interface{}) string {
	longName := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
	parts := strings.Split(longName, ".")
	if len(parts) > 0 {
		return parts[len(parts)-1]
	}
	return "?"
}

值 Value

设置值

SetXXX系列函数要求CanSet()返回true。

先判断类型在设置值

i := 123
iptr := &i
v := reflect.ValueOf(iptr).Elem()
if v.Kind() == reflect.Int {
	v.SetInt(321)
}
fmt.Println(i) // 321

不判断类型直接设置值

var i int = 123
iptr := &i
v := reflect.ValueOf(iptr).Elem()
v.Set(reflect.ValueOf(int(321)))
fmt.Println(i) // 321

可设置性

var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.

这里报错是因为 v 无法设置值给 x。

Value的 CanSet 方法提供了值的设置性;在这个例子中,

var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:" , v.CanSet()) // false

创建 v 时传递的是 x 的副本给 reflect.ValueOf(),而不是 x 本身。

var x float64 = 3.4
p := reflect.ValueOf(&x) // 注意:获取 X 的地址。
fmt.Println("type of p:", p.Type()) // type of p: *float64
fmt.Println("settability of p:" , p.CanSet()) // settability of p: false

反射对象 p 并不是可设置的,而且我们也不希望设置 p,实际上是 *p。为了获得 p 指向的内容,调用值上的 Elem 方法,从指针间接指向,然后保存反射值的结果叫做 v:

v := p.Elem()
fmt.Println("settability of v:" , v.CanSet()) // true
v.setFloat(7.1)

通过 reflect.Indirect() 方法来取指针指向的具体值更好。

设置和读取数组的值

arrval := reflect.ValueOf(&[...]int{1, 2, 3}).Elem()
arrval.Index(1).SetInt(5)

out := "["
for i := 0; i < arrval.Len(); i++ {
	out += strconv.Itoa(int(arrval.Index(i).Int())) + ","
}
out += "]"
fmt.Println(out) // [1,5,3,]

ValueOf()的参数需是数组指针,下面这个是错误的

reflect.ValueOf([...]int{1, 2, 3})

对于切片来说则不需要这么麻烦(因为通过切片获取到的数组是指针)

arrval := reflect.ValueOf([]int{1, 2, 3})
arrval.Index(1).SetInt(5)

通过指针设置值

var ip *int32
var i int32 = 123
vip := reflect.ValueOf(&ip)      // **int32
vi := reflect.ValueOf(&i).Elem() // 非得这样吗?(直接传递i给ValueOf会复制i)
vip.Elem().Set(vi.Addr()) // *int32.Set(*int32)
fmt.Println(*ip) // 123

将指针设置为零值

var i int32 = 123
ip := &i

vp := reflect.ValueOf(&ip).Elem()
vp.Set(reflect.Zero(vp.Type()))
fmt.Println(ip) // <nil>

设置map

设置map为空值

m := make(map[string]int)
m["a"], m["b"] = 1, 2

mp := reflect.ValueOf(&m).Elem()
mp.Set(reflect.Zero(mp.Type()))
fmt.Println(m)        // map[]
fmt.Println(m == nil) // true

interface{}

从匿名结构体通过Interface()还原到实际类型

b := struct{ a, b, c, d int64 }{1, 2, 3, 4}
v := reflect.ValueOf(b)
b1 := v.Interface().(struct {
	a, b, c, d int64
})
fmt.Println(b1) // {1 2 3 4}

interface()不可用于结构体中未导出字段

var st struct {
	PublicField  string
	privateField string
}

vSt := reflect.ValueOf(&st).Elem()
vSt.Field(0).IsValid() // true
vSt.Field(1).IsValid() // true

defer func() {
	if err := recover(); err != nil {
		fmt.Println("has error :", err)
	}
}()
//vSt.Field(0).Interface() // 没有panic
vSt.Field(1).Interface() // panic

标签Tag

var st struct {
	f1 string `tag1:"cont1" tag2:cont2 tag3:3`
}
var f1Tag = reflect.TypeOf(st).Field(0).Tag
f1Tag.Get("tag1") // cont1
f1Tag.Get("tag2") // 空字符串
f1Tag.Get("tag3") // 空字符串

reflect.Copy()

copy slice:

a := []int{1, 2, 3, 4, 5, 6}
b := []int{11, 12, 13, 14, 15}
aa := reflect.ValueOf(&a).Elem()
bb := reflect.ValueOf(&b).Elem()
aa.SetLen(4) // 限制长度只到第4个
reflect.Copy(bb, aa) // 赋值aa的前4个到b里
aa.SetLen(6) // 回复长度
fmt.Println(a) // [1 2 3 4 5 6]
fmt.Println(b) // [1 2 3 4 15]

copy array:

a := [...]int{1, 2, 3}
b := [...]int{11, 12, 13, 14, 15}
aa := reflect.ValueOf(&a).Elem()
bb := reflect.ValueOf(&b).Elem()
reflect.Copy(bb, aa)
fmt.Println(b) // [1 2 3 14 15]

结构体的字段

遍历结构体的字段

func main() {
	t := &T{"a", "b"}
	val := reflect.Indirect(reflect.ValueOf(t))
	typ := val.Type()

	for i := 0; i < val.NumField(); i++ {
		f := typ.Field(i)
		fmt.Println(f.Name)
	}
}

type T struct {
	PublicField  string
	privateField string
}

输出:

PublicField
privateField

判断结构体字段是否是导出的

t := &T{"a", "b"}
val := reflect.Indirect(reflect.ValueOf(t))
typ := val.Type()
typepath := typ.PkgPath()
fmt.Println("type pkgPath: ", typepath) // type pkgPath:  main

for i := 0; i < val.NumField(); i++ {
	f := typ.Field(i)
	p := f.PkgPath
	fmt.Println(f.Name, " => ", p)
}

//	 PublicField  =>
//	 privateField  =>  main

Select

文档: https://golang.org/pkg/reflect/#Select

和select结构功能一样,但这个可以直接指定数组。

测试代码:

import (
	"fmt"
	"reflect"
)

func main() {
	selectCase := make([]reflect.SelectCase, 3)

	for i := 0; i < 3; i++ {
		c := make(chan string)

		selectCase[i].Dir = reflect.SelectRecv
		selectCase[i].Chan = reflect.ValueOf(c)

		go func(i int, c chan string) {
			for j := 0; j < 5; j++ {
				c <- fmt.Sprintf("groutine#%d send %d", i, j)
			}
			fmt.Printf("groutine#%d closed\n", i)
			close(c)
		}(i, c)
	}

	done := 0
	finished := 0
	for finished < len(selectCase) {
		chosen, recv, recvOk := reflect.Select(selectCase)
		if recvOk {
			done++
			fmt.Printf("receive #%d, value=%s\n", chosen, recv.String())
		} else {
			fmt.Printf("#%d closed\n", chosen)
			selectCase = append(selectCase[:chosen], selectCase[chosen+1:]...)
		}
	}

	fmt.Println("done:", done) // 15
}

indirect

// From html/template/content.go
// Copyright 2011 The Go Authors. All rights reserved.
// indirect returns the value, after dereferencing as many times
// as necessary to reach the base type (or nil).
func indirect(a interface{}) interface{} {
	if a == nil {
		return nil
	}
	if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
		// Avoid creating a reflect.Value if it's not a pointer.
		return a
	}
	v := reflect.ValueOf(a)
	for v.Kind() == reflect.Ptr && !v.IsNil() {
		v = v.Elem()
	}
	return v.Interface()
}

© 著作权归作者所有

共有 人打赏支持
漂泊尘埃

漂泊尘埃

粉丝 5
博文 35
码字总数 70992
作品 0
朝阳
Golang 学习笔记(12)—— ORM实现

本文为转载,原文:Golang 学习笔记(12)—— ORM实现 Golang 介绍 本文将利用之前所学习到的内容实现一个简单的orm,实现比较简单,没有考虑过多的设计原则,以及性能安全之类的,只是单纯...

ChainZhang
01/10
0
0
Golang学习笔记目录

Golang 介绍 Go语言是谷歌2009发布的第二款开源编程语言。 Go语言专门针对多处理器系统应用程序的编程进行了优化,使用Go编译的程序可以媲美C或C++代码的速度,而且更加安全、支持并行进程。...

ChainZhang
2017/12/26
0
0
《计算机视觉-一种现代方法(第2版)》读书笔记二:图像形成

本篇思维导图 相关读书笔记 1.几何相机模型 1.1 图像形成 1.针孔透视(其实就是小时候玩的小孔成像) 2.弱透视(按比例正交) 在上图中,当d=Z0时,称为正交投影 3.带镜头的相机 4.人类的眼睛...

zhuxukang
03/19
0
0
java基础学习_反射、装饰模式、JDK新特性_day27总结

java基础学习反射、装饰模式、JDK新特性day27总结 ==================================================================================================================================......

黑泽明军
04/12
0
0
111 多线程JUC包下代码分析

Java多线程系列目录(共43篇) AtomicLongFieldUpdater:通过反射+CAS实现对传入对象的指定long字段实现类似AtomicLong的操作 http://www.cnblogs.com/skywang12345/p/javathreadscategory.ht...

素雷
2017/10/31
0
0
Java反射笔记

Java反射机制是指在运行状态中,对于任意一个类,都知道这个类的所有属性和方法;对于任意一个对象,都能调用它的属性和方法,反射功能十分的强大,但是使用反射的成本比较高。 Sun公司提供的...

黄步欢
2017/05/16
0
0
java语言反射的概述以及三种获取字节码文件对应的Class类型的对象的方式

反射的概述:   JAVA反射机制是在运行状态中,   对于任意一个类,都能够知道这个类的所有属性和方法(动态获取的信息);   对于任意一个对象,都能够调用它的任意一个方法和属性(动态调...

黑泽明军
04/12
0
0
C#基础系列——反射笔记

前言:使用反射也有几年了,但是一直觉得,反这个概念很抽象,今天有时间就来总结下这个知识点。 1、为什么需要反射:   最初使用反射的时候,作为小菜总是不理解,既然可以通过new 一个对...

嗯哼9925
2017/11/28
0
0
java&javaweb学习笔记(汇总)

我的java&javaweb学习笔记(汇总) 笔记分为两大部分:javase和javaweb javase javaweb 笔记内容主要是对一些基础特性和编程细节进行总结整理,适合了解java基础语法,想进一步深入学习的人 gi...

brianway
2016/02/09
1K
2
Java基础巩固笔记(1)-反射

Contents java基础巩固笔记(1)-反射 反射基本使用 数组的反射 配置文件加载 内省(Instropector) & JavaBean 反射:将类的属性和方法映射成相应的类。 反射基本使用 获取Class类的三种方法: 类...

卟想苌亣
2017/11/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

如何把你的Linux系统变的更加安全

做为一个小白,以为自己懂了点Linux知识,会搭建Linux各种服务就觉得自己牛的不要要的。在我们团队里面,我将使用了一台破电脑搭建Linux服务器,上面跑着Ftp服务存放着资源,ssh服务可以远程...

问题终结者
1分钟前
0
0
lombok的使用和原理

一、项目背景 在写Java程序的时候经常会遇到如下情形: 新建了一个Class类,然后在其中设置了几个字段,最后还需要花费很多时间来建立getter和setter方法 lombok项目的产生就是为了省去我们手...

颖辉小居
1分钟前
0
0
rsync至服务同步-系统日志-screen

rsync: 服务同步;配置文件:/etc/rsyncd.conf 默认端口:873 服务启动:rsync --daemon rsync -av /root/1.txt 192.168.1.2::test/2.txt (test为模块名称) /etc/rsync.conf配置样例: #指定...

ZHENG-JY
3分钟前
0
0
读取文件中内容转换成字符串

package com.lieni.ruyu.api.xmlTool; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.Unsuppo......

newdeng
3分钟前
0
0
《PHP和MySQL Web 开发》 第8章 设计Web数据库

LCL WARNING 这是我学习《PHP和MySQL Web 开发》的读书笔记,一些重要的知识点我会记录下来,当然只会写我觉得重要的。 如果有幸有人看到这个学习笔记了,你要结合着书看,不要光看这个笔记。...

十万猛虎下画山
10分钟前
0
0
Spring+jpaNo transactional EntityManager available

TransactionRequiredException: No transactional EntityManager availableEntityManager执行以下方法(refresh, persist, flush, joinTransaction, remove, merge) 都需要需要事务i......

wpfc
11分钟前
0
0
八幅漫画理解使用JSON Web Token设计单点登录系统

八幅漫画理解使用JSON Web Token设计单点登录系统 Sep 07, 2015 in Engineering 上次在《JSON Web Token - 在Web应用间安全地传递信息》中我提到了JSON Web Token可以用来设计单点登录系统。...

祖冲之
13分钟前
0
0
Spring框架中的设计模式(三)

Spring框架中的设计模式(三) 原创: 瑞查德-Jack 在之前的两篇文章中,我们看到了一些在Spring框架中实现的设计模式。这一次我们会发现这个流行框架使用的3种新模式。 本文将从描述两个创意...

瑞查德-Jack
16分钟前
1
0
[MicroPython]TPYBoard智能小车“飞奔的TPYBoard装甲一号”

智能小车作为现代的新发明,是以后的发展方向,他可以按照预先设定的模式在一个环境里自动的运作,不需要人为的管理,可应用于科学勘探等等的用途。智能小车能够实时显示时间、速度、里程,具...

bodasisiter
18分钟前
0
0
桌面虚拟化VDI(Virtual Desktop Infrastructure)

为了保证员工(客户)不把公司的资料复制、传输给别人。可以把员工平时办公放在服务器上做。所以使用桌面虚拟化。就是把一个服务器虚拟出很多桌面系统(如:windows)。 桌面虚拟化最大的优势...

王坤charlie
25分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部