马昌娟 發表於 2022-2-25 21:34:00

Go语言之反射

<h2>一、反射的基本概念</h2>
<h3>(一)什么是反射</h3>
<ul>
<li>反射可以再运行时动态获取变量的各种信息,比如变量的类型、值等</li>
<li>如果时结构体变量,还可以获取到结构体本身的各种信息,比如结构体的字段、方法</li>
<li>通过反射,还可以修改变量的值、调用方法</li>
</ul>
<p>不过使用反射,需要引入一个包:reflect</p>
<p>典型用法是用静态类型interface{}保存一个值,通过调用TypeOf获取其动态类型信息,该函数返回一个Type类型值。调用ValueOf函数返回一个Value类型值,该值代表运行时的数据。变量与反射类型之间的关系答题可以使用下图描述:</p>
<p><img src="https://img2022.cnblogs.com/blog/1137363/202202/1137363-20220223133521804-1476846949.png" alt="" loading="lazy" style="display: block; margin-left: auto; margin-right: auto"></p>
<h3>(二)反射的相关函数</h3>
<h4>1、reflect.Type类型</h4>
<p>通过reflect.TypeOf函数对接收的任意数据类型进行反射,此时返回的类型,它是一个接口:</p>
<div class="cnblogs_code">
<pre>type Type <span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)"> {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Kind返回该接口的具体分类</span>
<span style="color: rgba(0, 0, 0, 1)">    Kind() Kind
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Name返回该类型在自身包内的类型名,如果是未命名类型会返回""</span>
<span style="color: rgba(0, 0, 0, 1)">    Name() string
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> PkgPath返回类型的包路径,即明确指定包的import路径,如"encoding/base64"
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果类型为内建类型(string, error)或未命名类型(*T, struct{}, []int),会返回""</span>
<span style="color: rgba(0, 0, 0, 1)">    PkgPath() string
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回类型的字符串表示。该字符串可能会使用短包名(如用base64代替"encoding/base64")
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 也不保证每个类型的字符串表示不同。如果要比较两个类型是否相等,请直接用Type类型比较。</span>
<span style="color: rgba(0, 0, 0, 1)">    String() string
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回要保存一个该类型的值需要多少字节;类似unsafe.Sizeof</span>
<span style="color: rgba(0, 0, 0, 1)">    Size() uintptr
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回当从内存中申请一个该类型值时,会对齐的字节数</span>
    Align() <span style="color: rgba(0, 0, 255, 1)">int</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回当该类型作为结构体的字段时,会对齐的字节数</span>
    FieldAlign() <span style="color: rgba(0, 0, 255, 1)">int</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果该类型实现了u代表的接口,会返回真</span>
<span style="color: rgba(0, 0, 0, 1)">    Implements(u Type) bool
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果该类型的值可以直接赋值给u代表的类型,返回真</span>
<span style="color: rgba(0, 0, 0, 1)">    AssignableTo(u Type) bool
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如该类型的值可以转换为u代表的类型,返回真</span>
<span style="color: rgba(0, 0, 0, 1)">    ConvertibleTo(u Type) bool
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回该类型的字位数。如果该类型的Kind不是Int、Uint、Float或Complex,会panic</span>
    Bits() <span style="color: rgba(0, 0, 255, 1)">int</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回array类型的长度,如非数组类型将panic</span>
    Len() <span style="color: rgba(0, 0, 255, 1)">int</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回该类型的元素类型,如果该类型的Kind不是Array、Chan、Map、Ptr或Slice,会panic</span>
<span style="color: rgba(0, 0, 0, 1)">    Elem() Type
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回map类型的键的类型。如非映射类型将panic</span>
<span style="color: rgba(0, 0, 0, 1)">    Key() Type
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回一个channel类型的方向,如非通道类型将会panic</span>
<span style="color: rgba(0, 0, 0, 1)">    ChanDir() ChanDir
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回struct类型的字段数(匿名字段算作一个字段),如非结构体类型将panic</span>
    NumField() <span style="color: rgba(0, 0, 255, 1)">int</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回struct类型的第i个字段的类型,如非结构体或者i不在[0, NumField())内将会panic</span>
    Field(i <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">) StructField
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回索引序列指定的嵌套字段的类型,
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 等价于用索引中每个值链式调用本方法,如非结构体将会panic</span>
    FieldByIndex(index []<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">) StructField
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回该类型名为name的字段(会查找匿名字段及其子字段),
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 布尔值说明是否找到,如非结构体将panic</span>
<span style="color: rgba(0, 0, 0, 1)">    FieldByName(name string) (StructField, bool)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回该类型第一个字段名满足函数match的字段,布尔值说明是否找到,如非结构体将会panic</span>
<span style="color: rgba(0, 0, 0, 1)">    FieldByNameFunc(match func(string) bool) (StructField, bool)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果函数类型的最后一个输入参数是"..."形式的参数,IsVariadic返回真
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果这样,t.In(t.NumIn() - 1)返回参数的隐式的实际类型(声明类型的切片)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如非函数类型将panic</span>
<span style="color: rgba(0, 0, 0, 1)">    IsVariadic() bool
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回func类型的参数个数,如果不是函数,将会panic</span>
    NumIn() <span style="color: rgba(0, 0, 255, 1)">int</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回func类型的第i个参数的类型,如非函数或者i不在[0, NumIn())内将会panic</span>
    In(i <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">) Type
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回func类型的返回值个数,如果不是函数,将会panic</span>
    NumOut() <span style="color: rgba(0, 0, 255, 1)">int</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回func类型的第i个返回值的类型,如非函数或者i不在[0, NumOut())内将会panic</span>
    Out(i <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">) Type
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回该类型的方法集中方法的数目
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 匿名字段的方法会被计算;主体类型的方法会屏蔽匿名字段的同名方法;
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 匿名字段导致的歧义方法会滤除</span>
    NumMethod() <span style="color: rgba(0, 0, 255, 1)">int</span>
    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 返回该类型方法集中的第i个方法,i不在[0, NumMethod())范围内时,将导致panic
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 对非接口类型T或*T,返回值的Type字段和Func字段描述方法的未绑定函数状态
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 对接口类型,返回值的Type字段描述方法的签名,Func字段为nil</span>
    Method(<span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">) Method
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 根据方法名返回该类型方法集中的方法,使用一个布尔值说明是否发现该方法
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 对非接口类型T或*T,返回值的Type字段和Func字段描述方法的未绑定函数状态
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 对接口类型,返回值的Type字段描述方法的签名,Func字段为nil</span>
<span style="color: rgba(0, 0, 0, 1)">    MethodByName(string) (Method, bool)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 内含隐藏或非导出方法</span>
}</pre>
</div>
<p>所以如果获取到reflect.Type类型,然后调用其中的方法就可以获取被反射的数据类型的相应信息。</p>
<h4>2、reflect.Value</h4>
<p>&nbsp;这是一个结构体,为go值提供了反射接口:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">type Value struct {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 内含隐藏或非导出字段</span>
}</pre>
</div>
<p>有很多的方法可供使用:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">func (v Value) IsValid() bool
func (v Value) IsNil() bool
func (v Value) Kind() Kind
func (v Value) Type() Type
func (v Value) Convert(t Type) Value
func (v Value) Elem() Value
func (v Value) Bool() bool
func (v Value) Int() int64
func (v Value) OverflowInt(x int64) bool
func (v Value) Uint() uint64
func (v Value) OverflowUint(x uint64) bool
func (v Value) Float() float64
func (v Value) OverflowFloat(x float64) bool
func (v Value) Complex() complex128
func (v Value) OverflowComplex(x complex128) bool
func (v Value) Bytes() []</span><span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">
func (v Value) String() string
func (v Value) Pointer() uintptr
func (v Value) InterfaceData() [</span>2<span style="color: rgba(0, 0, 0, 1)">]uintptr
func (v Value) Slice(i, j </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">) Value
func (v Value) Slice3(i, j, k </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">) Value
func (v Value) Cap() </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">
func (v Value) Len() </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">
func (v Value) Index(i </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">) Value
func (v Value) MapIndex(key Value) Value
func (v Value) MapKeys() []Value
func (v Value) NumField() </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">
func (v Value) Field(i </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">) Value
func (v Value) FieldByIndex(index []</span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">) Value
func (v Value) FieldByName(name string) Value
func (v Value) FieldByNameFunc(match func(string) bool) Value
func (v Value) Recv() (x Value, ok bool)
func (v Value) TryRecv() (x Value, ok bool)
func (v Value) Send(x Value)
func (v Value) TrySend(x Value) bool
func (v Value) Close()
func (v Value) Call(</span><span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> []Value) []Value
func (v Value) CallSlice(</span><span style="color: rgba(0, 0, 255, 1)">in</span><span style="color: rgba(0, 0, 0, 1)"> []Value) []Value
func (v Value) NumMethod() </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">
func (v Value) Method(i </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">) Value
func (v Value) MethodByName(name string) Value
func (v Value) CanAddr() bool
func (v Value) Addr() Value
func (v Value) UnsafeAddr() uintptr
func (v Value) CanInterface() bool
func (v Value) Interface() (i interface{})
func (v Value) CanSet() bool
func (v Value) SetBool(x bool)
func (v Value) SetInt(x int64)
func (v Value) SetUint(x uint64)
func (v Value) SetFloat(x float64)
func (v Value) SetComplex(x complex128)
func (v Value) SetBytes(x []</span><span style="color: rgba(0, 0, 255, 1)">byte</span><span style="color: rgba(0, 0, 0, 1)">)
func (v Value) SetString(x string)
func (v Value) SetPointer(x unsafe.Pointer)
func (v Value) SetCap(n </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">)
func (v Value) SetLen(n </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">)
func (v Value) SetMapIndex(key, val Value)
func (v Value) Set(x Value)</span></pre>
</div>
<h4>3、reflect.Type、reflect.Value类型与变量之间的转换</h4>
<p>如果得到当前reflect.Value类型,那么如何将其转化为原始的变量类型,比如上述的User结构体类型:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">package</span><span style="color: rgba(0, 0, 0, 1)"> main

</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> (
    </span>"fmt"
    "reflect"<span style="color: rgba(0, 0, 0, 1)">
)

type User struct {
    NickName string
    Age      </span><span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">
}

func transType(v </span><span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)">{}) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用interface{}接收任意类型的反射
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 1、将interface{}转成reflect.Value类型</span>
    rVal :=<span style="color: rgba(0, 0, 0, 1)"> reflect.ValueOf(v)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 2、将reflect.Value转成interface{}</span>
    iVal :=<span style="color: rgba(0, 0, 0, 1)"> rVal.Interface()
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 3、通过断言将interface{}转成User类型</span>
    user :=<span style="color: rgba(0, 0, 0, 1)"> iVal.(User)

    fmt.Printf(</span>"原类型为%T", user) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">原类型为main.User</span>
<span style="color: rgba(0, 0, 0, 1)">
}

func main() {
    user :</span>=<span style="color: rgba(0, 0, 0, 1)"> User{
      NickName: </span>"lily"<span style="color: rgba(0, 0, 0, 1)">,
      Age:      </span>20<span style="color: rgba(0, 0, 0, 1)">,
    }

    transType(user)
}</span></pre>
</div>
<p>&nbsp;可以知道借助于中间interface{}变量完成转化:reflect.Value--&gt;interface{}--&gt;原类型</p>
<h4>3、reflect.Value与reflect.Type转换</h4>
<p>如果得到reflect.Value类型通过这个结构体中的&nbsp;func (Value)&nbsp;Type&nbsp;可以完成相互转化,从而调用reflect.Type类型中的方法:</p>
<p>&nbsp;</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">package</span><span style="color: rgba(0, 0, 0, 1)"> main

</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> (
    </span>"fmt"
    "reflect"<span style="color: rgba(0, 0, 0, 1)">
)

func transType(v </span><span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)">{}) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用interface{}接收任意类型的反射
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 1、将interface{}转成reflect.Value类型</span>
    rVal :=<span style="color: rgba(0, 0, 0, 1)"> reflect.ValueOf(v)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 2、将reflect.Value转成reflect.Type类型</span>
    rType :=<span style="color: rgba(0, 0, 0, 1)"> rVal.Type()
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 3、调用reflect.Type类型中的方法,获取类别</span>
    kind :=<span style="color: rgba(0, 0, 0, 1)"> rType.Kind()
    fmt.Print(kind)

}

func main() {
    var num </span>= 10<span style="color: rgba(0, 0, 0, 1)">

    transType(num)
}</span></pre>
</div>
<h3>(三)其它</h3>
<h4>1、Type和Kind之间的区别</h4>
<p>&nbsp;Type是类型,Kind是类别,从范围上说,Kind的范畴更大,Type和Kind可能是相同的,也可能是不同的,比如:</p>
<div class="cnblogs_code">
<pre>var num <span style="color: rgba(0, 0, 255, 1)">int</span> = 5 <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Type是int类型,Kind也是int类型</span>
var user User    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> user的Type是pkg.User,Kind是struct类型</span></pre>
</div>
<h4>&nbsp;2、通过反射获取变量的值</h4>
<p>在上面通过变量、interface{}、reflect.Value可以进行转换,但是反射得到reflect.Value类型,并没有获取对应变量或者字段的值,所以这里看一下如何获取值。</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">package</span><span style="color: rgba(0, 0, 0, 1)"> main

</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> (
    </span>"fmt"
    "reflect"<span style="color: rgba(0, 0, 0, 1)">
)

func transType(v </span><span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)">{}) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用interface{}接收任意类型的反射
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 1、将interface{}转成reflect.Value类型</span>
    rVal :=<span style="color: rgba(0, 0, 0, 1)"> reflect.ValueOf(v)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 2、将reflect.Value调用对应的方法获取值</span>
    value :=<span style="color: rgba(0, 0, 0, 1)"> rVal.Int()
    fmt.Println(value) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 10</span>
<span style="color: rgba(0, 0, 0, 1)">
}

func main() {
    var num </span>= 10<span style="color: rgba(0, 0, 0, 1)">

    transType(num)
}</span></pre>
</div>
<p>通过反射获取变量的值,要求数据类型匹配,比如num是int类型,那么在反射中就需要使用reflect.ValueOf(v).Int()方法。</p>
<h4>3、通过反射修改值</h4>
<p>通过反射来修改变量的值,那么一定要传入变量的指针类型呢,这样才能修改原来的值。同时需要使用到reflect.ValueOf(v).Elem()方法</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">package</span><span style="color: rgba(0, 0, 0, 1)"> main

</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> (
    </span>"fmt"
    "reflect"<span style="color: rgba(0, 0, 0, 1)">
)

func transSet(v </span><span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)">{}) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用interface{}接收任意类型的反射
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 1、将interface{}转成reflect.Value类型</span>
    rVal :=<span style="color: rgba(0, 0, 0, 1)"> reflect.ValueOf(v)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 2、将reflect.Value调用对应的方法修改值</span>
    rVal.Elem().SetInt(20<span style="color: rgba(0, 0, 0, 1)">)

}

func main() {
    var num </span>= 10<span style="color: rgba(0, 0, 0, 1)">
    transSet(</span>&amp;<span style="color: rgba(0, 0, 0, 1)">num)
    fmt.Println(num) </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 20</span>
}</pre>
</div>
<p>在上面的reflect.ValueOf(v).Elem()可以理解为获取指针变量:</p>
<div class="cnblogs_code">
<pre>var num = 10<span style="color: rgba(0, 0, 0, 1)">
var value </span>*<span style="color: rgba(0, 0, 255, 1)">int</span>= &amp;<span style="color: rgba(0, 0, 0, 1)">num
</span>*value = 20</pre>
</div>
<h2>二、反射最佳实践&nbsp;</h2>
<p>通过反射获取结构体字段、方法及调用,并且获取字段标签:</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">package</span><span style="color: rgba(0, 0, 0, 1)"> main

</span><span style="color: rgba(0, 0, 255, 1)">import</span><span style="color: rgba(0, 0, 0, 1)"> (
    </span>"fmt"
    "reflect"<span style="color: rgba(0, 0, 0, 1)">
)

type User struct {
    UserName string `json:</span>"username"<span style="color: rgba(0, 0, 0, 1)">`
    Age      </span><span style="color: rgba(0, 0, 255, 1)">int</span>    `json:"age"<span style="color: rgba(0, 0, 0, 1)">`
}

func (u User) GetUser() {
    fmt.Println(</span>"获取用户信息"<span style="color: rgba(0, 0, 0, 1)">)
}

func (u User) GetAge(age </span><span style="color: rgba(0, 0, 255, 1)">int</span>, num <span style="color: rgba(0, 0, 255, 1)">int</span>) <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> {
    lastAge :</span>= age +<span style="color: rgba(0, 0, 0, 1)"> num
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> lastAge
}

func TestRefStru(v </span><span style="color: rgba(0, 0, 255, 1)">interface</span><span style="color: rgba(0, 0, 0, 1)">{}) {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 对结构体变量进行反射
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取reflect.Type类型</span>
    rType :=<span style="color: rgba(0, 0, 0, 1)"> reflect.TypeOf(v)
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取reflect.Value类型</span>
    rVal :=<span style="color: rgba(0, 0, 0, 1)"> reflect.ValueOf(v)

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取v对应的类别</span>
    kd :=<span style="color: rgba(0, 0, 0, 1)"> rVal.Kind()

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果传入的非struct就退出</span>
    <span style="color: rgba(0, 0, 255, 1)">if</span> kd !=<span style="color: rgba(0, 0, 0, 1)"> reflect.Struct {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
    }

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取结构体中有多少个字段</span>
    FieldNum :=<span style="color: rgba(0, 0, 0, 1)"> rVal.NumField()

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 对字段的tag进行反射,获取tag标签值</span>
    <span style="color: rgba(0, 0, 255, 1)">for</span> i := 0; i &lt; FieldNum; i++<span style="color: rgba(0, 0, 0, 1)"> {
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取struct标签,通过reflect.Type来获取tag标签值</span>
      tagVal := rType.Field(i).Tag.Get("json"<span style="color: rgba(0, 0, 0, 1)">)
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果字段存在就打印出来</span>
      <span style="color: rgba(0, 0, 255, 1)">if</span> tagVal != ""<span style="color: rgba(0, 0, 0, 1)"> {
            fmt.Printf(</span>"tag:%v \n"<span style="color: rgba(0, 0, 0, 1)">, tagVal)
      }
    }

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取结构体中有多少个方法</span>
    MethodNum :=<span style="color: rgba(0, 0, 0, 1)"> rVal.NumMethod()
    fmt.Println(</span>"方法个数为:"<span style="color: rgba(0, 0, 0, 1)">, MethodNum)

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 方法排序是按照ASCII排序,与在结构体中的顺序无关
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 该方法无参数的调用</span>
    rVal.Method(1).Call(nil) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">调用第一个方法

    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 含参数的调用,注意Call方法传入的的[]reflect.Value类型的参数,返回值是[]reflect.Value类型</span>
<span style="color: rgba(0, 0, 0, 1)">    var parames []reflect.Value
    parames </span>= append(parames, reflect.ValueOf(20<span style="color: rgba(0, 0, 0, 1)">))
    parames </span>= append(parames, reflect.ValueOf(5<span style="color: rgba(0, 0, 0, 1)">))
    res :</span>= rVal.Method(0<span style="color: rgba(0, 0, 0, 1)">).Call(parames)
    fmt.Println(</span>"年龄", res.Int())
}

func main() {
    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建一个User实例</span>
    u :=<span style="color: rgba(0, 0, 0, 1)"> User{
      UserName: </span>"lily"<span style="color: rgba(0, 0, 0, 1)">,
      Age:      </span>20<span style="color: rgba(0, 0, 0, 1)">,
    }
    TestRefStru(u)
}</span></pre>
</div>
<p>&nbsp;</p>

</div>
<div id="MySignature" role="contentinfo">
    <div>作者:iveBoy</div>
<div>出处:http://www.cnblogs.com/shenjianping/</div>
<div>本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。 </div><br><br>
来源:https://www.cnblogs.com/shenjianping/p/15925068.html
頁: [1]
查看完整版本: Go语言之反射