Al igual que el lenguaje Java, Go también implementa la reflexión en tiempo de ejecución, lo que nos brinda la capacidad de manipular cualquier tipo de objeto en tiempo de ejecución. Por ejemplo, podemos ver el tipo específico de una variable de interfaz, ver cuántos campos tiene una estructura, cómo modificar el valor de un campo, etc.
TypeOf 和 ValueOf
En la definición de reflexión de Go, cualquier interfaz se compone de dos partes, una es el tipo específico de interfaz y la otra es el valor correspondiente al tipo específico. Por ejemplo var i int = 3
, debido a que interface{}
puede representar cualquier tipo, la variable i
se puede convertir a interface{}
, por lo que la variable se puede considerar i
como una interfaz, entonces la representación de esta variable en la reflexión de Go es <Value,Type>
, donde Valor es el valor de la variable 3
y el Tipo variable es el tipo int
.
En la reflexión de Go, la biblioteca estándar nos proporciona dos tipos para representar su reflect.Value
suma reflect.Type
y dos funciones para obtener la Value
suma de cualquier objeto Type
.
func main() {
u:= User{"张三",20}
t:=reflect.TypeOf(u)
fmt.Println(t)
}
type User struct{
Name string
Age int
}
reflect.TypeOf
Puede obtener el tipo específico de cualquier objeto, y puede ver main.User
este tipo de estructura imprimiendo aquí . reflect.TypeOf
La función acepta una interfaz vacía interface{}
como parámetro, por lo que este método puede aceptar cualquier tipo de objeto.
Siguiendo el ejemplo anterior, veamos cómo hacer que un objeto se refleje Value
.
v:=reflect.ValueOf(u)
fmt.Println(v)
Al igual que la TypeOf
función, también puede aceptar cualquier objeto, puede ver la impresión como {张三 20}
. Para los dos tipos de salida anteriores, Go language también fmt.Printf
nos proporciona métodos convenientes a través de funciones.
fmt.Printf("%T\n",u)
fmt.Printf("%v\n",u)
Este ejemplo es el mismo que el resultado del ejemplo anterior.
reflexionar Valor al tipo primitivo
En el ejemplo anterior, podemos reflect.ValueOf
convertir cualquier tipo de objeto en uno a través de una función reflect.Value
, por lo que si queremos revertirlo, en realidad es posible y reflect.Value
nos proporciona una Inteface
forma de ayudarnos a hacer esto. Continúe con el ejemplo anterior:
u1:=v.Interface().(User)
fmt.Println(u1)
De esta manera, volvemos al User
objeto original , que puede ser verificado por la salida impresa. La razón aquí es debido a la reducción en la reflexión de Go's, los sujetos se dividieron en cualquiera reflect.Value
y reflect.Type
, mientras reflect.Value
sostienen simultáneamente un objeto reflect.Value
y reflect.Type
así podemos ser reflect.Value
una Interface
forma de lograr la reducción. Ahora vemos cómo reflect.Value
obtener el correspondiente de uno reflect.Type
.
t1:=v.Type()
fmt.Println(t1)
En el ejemplo anterior, mediante reflect.Value
el Type
método se puede utilizar para obtener el correspondiente reflect.Type
.
Obtenga el tipo subyacente del tipo
¿Qué significa el tipo subyacente? De hecho, los principales tipos correspondientes son tipos básicos, interfaces, estructuras, punteros, etc., porque podemos type
declarar muchos tipos nuevos a través de palabras clave. Por ejemplo, en el ejemplo anterior, u
el tipo real del objeto es User
, pero el subyacente correspondiente type es struct
este tipo de estructura. Vamos a verificarlo.
fmt.Println(t.Kind())
Se Kind
puede obtener mediante métodos, lo cual es muy sencillo, por supuesto, también podemos utilizar métodos Value
objeto Kind
, son equivalentes.
Las notas de lectura "Go Language Practice" continuarán, escanee el código QR para seguir la cuenta
flysnow_org
oficial o el sitio web http://www.flysnow.org/ , y lea las notas de seguimiento por primera vez. Si lo encuentra útil, compártalo con su círculo de amigos, gracias por su apoyo.
Go language proporciona los siguientes tipos de nivel más bajo, como puede ver, todos son los más básicos.
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
)
Iterar sobre campos y métodos
A través de la reflexión, podemos obtener el campo de un tipo de estructura, y también el método de exportación de un tipo, de modo que podamos entender la estructura de un tipo en tiempo de ejecución, que es una función muy poderosa.
for i:=0;i<t.NumField();i++ {
fmt.Println(t.Field(i).Name)
}
for i:=0;i<t.NumMethod() ;i++ {
fmt.Println(t.Method(i).Name)
}
Este ejemplo imprime todos los nombres de campo de la estructura y el método de la estructura. NumField
El método obtiene cuántos campos hay en la estructura y luego Field
pasa el índice a través del método, obtiene cada campo en un bucle y luego imprime sus nombres.
El mismo método es similar, así que no lo repetiré aquí.
Modificar el valor del campo
¿Qué pasa si queremos modificar dinámicamente el valor de un campo durante la operación? Una es que proporcionamos métodos o campos exportados de forma rutinaria para que modifiquemos, y la otra es utilizar la reflexión, que se presenta principalmente aquí.
func main() {
x:=2
v:=reflect.ValueOf(&x)
v.Elem().SetInt(100)
fmt.Println(x)
}
Lo anterior es un ejemplo de cómo modificar una variable mediante la reflexión.
Debido a que la reflect.ValueOf
función devuelve una copia del valor, la premisa es que pasamos la dirección de la variable a modificar. En segundo lugar, necesitamos llamar al Elem
método para encontrar el valor al que apunta este puntero. Finalmente, podemos usar el SetInt
método para modificar el valor.
Hay varios puntos importantes anteriores para garantizar que el valor se pueda modificar y Value
nos proporciona un CanSet
método para ayudarnos a determinar si el objeto se puede modificar.
Ahora podemos actualizar el valor de la variable, entonces, ¿cómo modificamos el valor del campo de estructura? Inténtalo tú mismo.
Método de llamada dinámica
No solo podemos llamar al método de estructura normalmente, sino que también podemos usar la reflexión. Para realizar una llamada de reflexión, primero debemos obtener el método que se necesita llamar y luego realizar una llamada de parámetro, como se muestra en el siguiente ejemplo:
func main() {
u:=User{"张三",20}
v:=reflect.ValueOf(u)
mPrint:=v.MethodByName("Print")
args:=[]reflect.Value{reflect.ValueOf("前缀")}
fmt.Println(mPrint.Call(args))
}
type User struct{
Name string
Age int
}
func (u User) Print(prfix string){
fmt.Printf("%s:Name is %s,Age is %d",prfix,u.Name,u.Age)
}
MethodByName
El método nos permite obtener un objeto de método basado en un nombre de método, y luego construimos los parámetros requeridos por el método, y finalmente lo llamamos Call
para lograr el propósito de llamar dinámicamente al método.
Podemos utilizar el método obtenido IsValid
para juzgar si está disponible (existente).
El parámetro aquí es una Value
matriz de tipo, por lo que los parámetros requeridos deben ValueOf
convertirse a través de funciones.
Este es el final de la introducción básica de la reflexión. El próximo artículo presentará algunos usos avanzados, como obtener la etiqueta de un campo. De uso común, como convertir una cadena json en una estructura, se usa la etiqueta del campo.