Go反射
Posted by 付辉 on Thursday, May 31, 2018 共1277字
人生的挫折感并不取决于境遇本身,而取决于境遇和自我期待之间的落差。
概述
对interface
类型操作,如何对内部的值进行处理和分析。比如判断interface
是否底层存储的是struct
类型,以及该struct
是否含有某个特定的Field
值。
interface
类型包含两部分内容:dynamic type
和dynamic value
。当转换为interface
类型后(操作是默认的),原类型下声明的方法,interface
类型就无法再调用了。
实际工作中,interface
类型会接收任意类型的值,处理的过程很多都是通过reflect
实现的。
reflect.Value
reflect
里两个主要角色:Value
和Type
。Value
用于处理值的操作,反射过程中处理的值是原始值的值拷贝,所以操作中要注意区分值传递和地址传递。
对于指针类型的值,只有获取其原始值,才可以达到修改的目的。如下所示,obj
实际类型是一个struct
的指针,想要将其转换成“值类型”,调用Elem
方法来实现。
//获取指针的实际类型
v := reflect.ValueOf(obj) //Kind == Ptr
v = v.Elem()
if v.Kind() != reflect.Struct {
return NewError(http.ErrorInvalidParam, "interface类型必须是struct结构", nil)
}
一些其他的操作,比如通过reflect.Value
获取reflect.Type
类型,通过诸如Index
、Elem
、MapIndex
、Field
等来操作不同的数据类型,当然调用前最好结合Kind
对实际类型进行判断,保证调用的安全性。
查找指定的Field
我们假设struct
中包含有某个特殊Field
,那么在接口层面该如何进行判断呢?比如,查看结构体中是否含有Data
的Field
.
reflect
本身提供了多种判断形式。以FieldByName
为例,Type
和Value
都实现了该方法,但返回值不相同。reflect
要求调用的值本身需要是struct
类型才可以。
h := v.FieldByName(HeaderHField) //HeaderHField为自定义常亮
if h.IsValid() {
}
将value
转换为interface
类型
reflect
操作的interface
类型,即由interface
转换为reflect.Value
类型,同样,逆向的转换也是可以的。
它提供了interface()
方法。转换之后,我们就可以继续使用断言进行实际类型转换了。
value := h.Interface() //将value转换为interface
customHead, isOk := value.(string) // 断言为string类型
安全设置结构体中的值
为了使反射过程变得更安全,需要了解几个函数
CanAddr reports whether the value's address can be obtained with Addr. Such values are called addressable. A value is addressable if it is an element of a slice, an element of an addressable array, a field of an addressable struct, or the result of dereferencing a pointer. If CanAddr returns false, calling Addr will panic.
CanSet reports whether the value of v can be changed. A Value can be changed only if it is addressable and was not obtained by the use of unexported struct fields. If CanSet returns false, calling Set or any type-specific setter (e.g., SetBool, SetInt) will panic.
在使用函数时,还要注意到函数内部的限制。比如FieldByName
要求作用的对象类型必须是结构体。所以,在操作之前一定要做好类型判断:v.Kind() != reflect.Struct
。
// FieldByName returns the struct field with the given name.
// It returns the zero Value if no field was found.
// It panics if v's Kind is not struct.
func (v Value) FieldByName(name string) Value {
v.mustBe(Struct)
if f, ok := v.typ.FieldByName(name); ok {
return v.FieldByIndex(f.Index)
}
return Value{}
}
参考文章: