返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

reflect 1.18

发布于 2024-10-12 19:15:53 字数 8489 浏览 0 评论 0 收藏 0

反射可在运行期 动态 获取类型(type)和值(value)信息。

  • 代码繁琐,可读性差。
  • 性能较差,不及 Unsafe 指针。
func TypeOf(i any) Type
func ValueOf(i any) Value

类型(Type)表示具体的静态类型,而类别(Kind)则表示其底层结构。
相比 Type,Value 更倾向于对值的处理。两者有许多同名方法,但返回值可能不同。

package main

import (
	. "reflect"
)

func main() {
	type X int
	var x X = 1

	v := ValueOf(x)
	t := v.Type()

	println(t.PkgPath() + "." + t.Name())  // main.X

	switch v.Kind() {
	case Int: println(v.Int())         // 1
	case String: println(v.String())
	}
    
    // -------------------------------------

	var y int = 2
	ty := TypeOf(y)
    
	println(t == ty)                 // false
	println(t.Kind() == ty.Kind())   // true
}

如果是 nil 接口,那么无法反射。
因为接口须和实现类型结合,才能有完整 itab 信息。

func main() {
	type Xer interface {
		A()
		B()
	}

	var i Xer

	t := TypeOf(i)
	println(t != nil)     // false

	v := ValueOf(i)
	println(v.IsValid())  // false
    // println(v.Type())  // panic!
    
    // -------------------------------
    
    // 注意,参数是指针而非接口。

    t = TypeOf((*Xer)(nil))
    println(t != nil)         // true
    println(t.Elem().Name())  // Xer
}
func main() {
	var i interface{} = (*int)(nil)

	t := TypeOf(i)
	println(t != nil)     // true

	v := ValueOf(i)
	println(v.IsValid())  // true
	println(v.Type())     // ok!
}

某些方法返回的是零值,需要进行验证。

  • IsValid :验证 Value 自身是否为零值(zero Value)。
  • IsZeroIsNil :验证目标对象(zero/nil value)。
func main() {
    
	// zero Value ----------------------
    
	var v Value
	println(v.IsValid())     // false
    
	// println(v.IsZero())   // panic!
	// println(v.IsNil())    // panic!

	// zero value ----------------------
    
	v = ValueOf([]int{})
	println(v.IsValid())	 // true
	println(v.IsZero())      // false

	var i interface{} = (*int)(nil)
	v = ValueOf(i)
	println(v.IsValid())	 // true
	println(v.IsNil())       // true
}

复合类型

除通过实例反射外,还可直接构造一些复合类型。

package main

import (
	"fmt"
	. "reflect"
)

func main() {

	tByte   := TypeOf(byte(0))
	tInt    := TypeOf(0)
	tString := TypeOf("")
	tSlice  := TypeOf([]int{})

    // --------------------------------
    
	a := ArrayOf(10, tByte)      // [10]uint8
	c := ChanOf(BothDir, tInt)   // chan int
	m := MapOf(tString, tInt)    // map[string]int
	s := SliceOf(tInt)           // []int
	p := PointerTo(tInt)         // *int

	// x := StructOf(...)

    // ---------------------------------
    
	in  := []Type{ tInt, tInt, tSlice }
	out := []Type{ tString }
	f   := FuncOf(in, out, true)  // func(int, int, ...int) string
    
	fmt.Println(a, c, m, s, p, f)  
}

另外,值和指针不是同一类型。
方法 Elem 返回指针、数组、切片、字典或通道的基类型。

func main() {
    x := 100
    t, p := TypeOf(x), TypeOf(&x)

    fmt.Println(t, p, t == p)         // int  *int  false
    fmt.Println(t.Kind(), p.Kind())   // int   ptr
    fmt.Println(t == p.Elem())        // true
}
func main() {
	x := 100

	p := TypeOf(&x)
	s := TypeOf([]int32{})
	m := TypeOf(map[string]int{})

    fmt.Println(p.Elem())   // int
    fmt.Println(s.Elem())   // int32

    fmt.Println(m.Key())    // string
    fmt.Println(m.Elem())   // int
}

结构体

遍历结构体字段,包括 匿名嵌入 (anonymous embed)和 非导出 (unexported)字段。

type U struct {
    name string
    age  int
}

type M struct {
    U
    title string
}

// -----------------------------------

func pp(t Type, prefix string) {
	for i := 0; i < t.NumField(); i++ {
		f := t.Field(i)
		fmt.Println(prefix, f.Name)

		if f.Anonymous { pp(f.Type, prefix + "  ") }
	}
}

// -----------------------------------

func main() {
    var m M

    t := TypeOf(&m)
    if t.Kind() == Ptr {
        t = t.Elem()
    }

    pp(t, "")
}

/*

U
  name
  age
title

*/

可按多级索引查找。
只是 FieldByName 不支持多级名称。

type U struct {
    name string      // 0
    age  int         // 1
}

type M struct {
    U                // 0
    title string     // 1
}

// ----------------------------------

func main() {
    var m M
    t := TypeOf(m)

	// 按名称查找。
    name, _ := t.FieldByName("name")        
    fmt.Println(name.Name, name.Offset)   // name  0

	// 按多级索引查找。
    age := t.FieldByIndex([]int{0, 1})      
    fmt.Println(age.Name, age.Offset)     // age  16
}

提取 tag,自动分解。

type User struct {
	id   int    `field:"uid" type:"int"`
	name string `field:"username" type:"varchar(50)"`
}

// -----------------------

func main() {
    var u User
    t := TypeOf(u)

    for i := 0; i < t.NumField(); i++ {
        f := t.Field(i)
        fmt.Println(f.Name, f.Tag.Get("field"), f.Tag.Get("type"))
    }
}

/*

id: uid int
name: username varchar(50)

*/

方法集

获取 方法集 (method set)内 导出 (exported)方法。

type N int

func  (N) X() {}
func (*N) Y() {}

// -----------------------

type M struct {
	N
}

func  (M) A() {}
func (*M) b() {}
func (*M) C() {}

// -----------------------

func main() {
	t := TypeOf((*M)(nil))

	for i := 0; i < t.NumMethod(); i++ {
		fmt.Println(t.Method(i))
	}

	if m, ok := t.MethodByName("C"); ok {
		fmt.Println(m)
	}
}

/*

{A  func(*main.M) <func(*main.M) Value> 0}
{C  func(*main.M) <func(*main.M) Value> 1}
{X  func(*main.M) <func(*main.M) Value> 2}
{Y  func(*main.M) <func(*main.M) Value> 3}

{C  func(*main.M) <func(*main.M) Value> 1}

*/

在官方文档里,有句让人费解的话。

type Type interface {
    // counts unexported methods only for interface types.
	NumMethod() int    
}
func main() {
	var i interface {
		A()
		b()
		C()
	}
    
    // 传入的是指针(*i),而非接口(i)。
    // 通过 Elem 获取接口声明的方法。

    // t := TypeOf(&i)        // 0
    t := TypeOf(&i).Elem()    // 3

	for i := 0; i < t.NumMethod(); i++ {
		fmt.Println(t.Method(i))
	}    
}

/*

{A  func() <invalid Value> 0}
{C  func() <invalid Value> 1}
{b  func() <invalid Value> 2}

*/

接口

围绕接口的辅助方法,作一些必要判断。

func main() {
	a := 100
	v := ValueOf(&a)

	if t := TypeOf((*int)(nil)); v.Type().ConvertibleTo(t) {
		if p, ok := v.Interface().(*int); ok {
			*p += 100
			fmt.Println(*p, a)   // 200 200
		}
	}
}
type X int
func (X) String() string { return "" }

func main() {
    var x X
    t := TypeOf(x)

    st := TypeOf((*fmt.Stringer)(nil)).Elem()
    fmt.Println(t.Implements(st))              // true
    fmt.Println(t.AssignableTo(st))            // true
}
func main() {
	a := 100

    // 反射参数是接口(any)。
    // 所以 iface.data 不能寻址和修改。
    // 除非传入指针,则原对象(v.Elem)可改。
    
	v := ValueOf(a)
	p := ValueOf(&a)

	// iface.data
    fmt.Println(v.CanAddr(), v.CanSet())  // false false
    fmt.Println(p.CanAddr(), p.CanSet())  // false false

    // ----------------------------------
    
    // *data
    e := p.Elem()
    fmt.Println(e.CanAddr(), e.CanSet())  // true true

    if e.CanSet() {
    	e.SetInt(200)
    	fmt.Println(a)  // 200
	}

	if e.CanAddr() {
		*(*int)(unsafe.Pointer(e.UnsafeAddr())) = 300
    	fmt.Println(a)  // 300
	}
}

调用

动态方法调用。

type X int

func (x X) Add(y int) int {
    return int(x) + y
}

func (x X) Print(format string, y ...any) {
	fmt.Printf(format, y...)
}

// -------------------------------

func main() {
    var x X = 100
    v := ValueOf(&x)

    func() {
	    m := v.MethodByName("Add")
        
	    in := []Value{ ValueOf(1) }
	    out := m.Call(in)

	    for _, v := range out {
	        fmt.Println(v)
	    }
	}()

    // 变参
    func() {
	    m := v.MethodByName("Print")
        
	    in := []Value{ 
	    	ValueOf("%d,%d,%d\n"), 
	    	ValueOf([]any{ 1, 2, 3 }),
	    }
        
	    out := m.CallSlice(in)

	    for _, v := range out {
	        fmt.Println(v)
	    }
	}()
}

创建

创建对象实例(分配内存),对应 newmake 内置函数。

func main() {

	// --- New -------------------------

	v := New(TypeOf(0))    // new(int)
	v.Elem().SetInt(100)
	fmt.Println(v.Elem().Int())    // 100

	// --- NewAt ------------------------

	p := new(int)
	v = NewAt(TypeOf(0), unsafe.Pointer(p))
	v.Elem().SetInt(200)
	fmt.Println(v.Elem().Int(), *p)   // 200  200

	// --- Make -------------------------

	v = MakeSlice(TypeOf([]int{}), 0, 10)
	v = Append(v, ValueOf(1), ValueOf(2))
	fmt.Println(v)    // [1, 2]

	// MakeMap, MakeMapWithSize, MakeChan
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文