📜  Golang 中的反射

📅  最后修改于: 2021-10-24 13:14:44             🧑  作者: Mango

反射是程序在运行时内省和分析其结构的能力。在 Go 语言中,反射主要是通过类型来进行的反射包为此目的提供了所有必需的 API/方法。反射通常被称为元编程的一种方法为了更好地理解反射,让我们先了解一下空接口:

当我们声明一个具有未知参数和数据类型的函数时,空接口非常有用。 Println、Printf 等库方法以空接口作为参数。空接口具有某些赋予其功能的隐藏属性。数据以下列方式抽象。

Golang 中的空接口

示例 1:

// Undersyanding Empty Interfaces in Golang
package main
  
import (
    "fmt"
)
  
  
func observe(i interface{}) {
      
    // using the format specifier 
    // %T to check type in interface
    fmt.Printf("The type passed is: %T\n", i)
      
    // using the format specifier %#v 
    // to check value in interface
    fmt.Printf("The value passed is: %#v \n", i)
    fmt.Println("-------------------------------------")
}
  
func main() {
    var value float64 = 15
    value2 := "GeeksForGeeks"
    observe(value)
    observe(value2)
}

输出:

The type passed is: float64
The value passed is: 15 
-------------------------------------
The type passed is: string
The value passed is: "GeeksForGeeks" 
-------------------------------------

在这里我们可以清楚地看到,一个空的接口将能够接受任何参数并适应其值和数据类型。这包括但不限于Structs 和指向 Structs 的指针。

什么是反思的必要性?

通常,传递给这些空接口的数据不是原语。例如,它们也可能是结构。我们需要在不知道它们的类型或其中存在的值的情况下对这些数据执行程序。在这种情况下,为了对结构执行各种操作,例如解释其中存在的数据以查询数据库或为数据库创建模式,我们需要知道其中存在的类型以及字段的数量。这些问题可以在运行时使用反射来处理

反射包:

Go 反射的基础是基于Values、Types 和 Kinds 。这些在包中定义,类型为reflect.Value、reflect.Typereflect.Kind ,可以使用方法获取

  1. 反映.ValueOf(x 接口{})。
  2. 反映.TypeOf(x 接口{})。
  3. 类型.种类()。
Type Kind
A Type is the representation of a type in Go.For example in user defined/custom types in go, the name assigned by the user is stored in as Type. A Kind is the representation of the type of a Type. For example in user defined/custom types, the data type of the Type will be the Kind.
The Type can be obtained using reflect.TypeOf(x interface{}) The Kind can be obtained using .Kind()

反射包为我们提供了许多其他方法:

  1. NumField():此方法返回结构中存在的字段数。如果传递的参数不是那种反射.结构那么它就会恐慌。
  2. Field():此方法允许我们使用索引变量访问结构中的每个字段。

在下面的示例中,我们将发现结构体的 Kind 和 Type 与另一种自定义数据类型之间的区别。除此之外,我们将使用反射包中的方法来获取和打印结构中的字段以及自定义数据类型中的值。

示例 2:

// Example program to show difference between
// Type and Kind and to demonstrate use of
// Methods provided by Go reflect Package
package main
  
import (
    "fmt"
    "reflect"
)
  
type details struct {
    fname   string
    lname   string
    age     int
    balance float64
}
  
type myType string
  
func showDetails(i, j interface{}) {
    t1 := reflect.TypeOf(i)
    k1 := t1.Kind()
    t2 := reflect.TypeOf(j)
    k2 := t2.Kind()
    fmt.Println("Type of first interface:", t1)
    fmt.Println("Kind of frst interface:", k1)
    fmt.Println("Type of second interface:", t2)
    fmt.Println("Kind of second interface:", k2)
      
    fmt.Println("The values in the first argument are :")
    if reflect.ValueOf(i).Kind() == reflect.Struct {
        value := reflect.ValueOf(i)
        numberOfFields := value.NumField()
        for i := 0; i < numberOfFields; i++ {
            fmt.Printf("%d.Type:%T || Value:%#v\n", 
              (i + 1), value.Field(i), value.Field(i))
              
            fmt.Println("Kind is ", value.Field(i).Kind())
        }
    }
    value := reflect.ValueOf(j)
    fmt.Printf("The Value passed in "+
      "second parameter is %#v", value)
}
  
func main() {
    iD := myType("12345678")
    person := details{
        fname:   "Go",
        lname:   "Geek",
        age:     32,
        balance: 33000.203,
    }
    showDetails(person, iD)
}

输出:

Type of first interface: main.details
Kind of frst interface: struct
Type of second interface: main.myType
Kind of second interface: string
The values in the first argument are :

1.Type:reflect.Value || Value:"Go"
Kind is  string
2.Type:reflect.Value || Value:"Geek"
Kind is  string
3.Type:reflect.Value || Value:32
Kind is  int
4.Type:reflect.Value || Value:33000.203
Kind is  float64
The Value passed in second parameter is "12345678"

在上面的例子中,我们向函数showDetails()传递了两个参数,该函数将空接口作为参数。论据是:

  1. person ,这是一个结构体。
  2. iD ,它是一个字符串。

我们使用了方法reflect.TypeOf(i interface{})Type.Kind()方法来获取这些字段,我们可以看到输出中的差异。

  • struct person 的类型为 main.details和 Kind reflect.Struct
  • 变量 iD 的类型为 main.myTypeKind 字符串

因此 Type 和 Kind 之间的区别变得清晰,并且是根据它们的定义。这是 Go 语言反射中的一个基本概念。此外,我们已经使用的方法reflect.ValueOf() .NumField().Field()从反映封装以及在空接口来获得的值,每个字段分别struct的字段的数量,然后。这是可能的,因为在运行时使用反射允许我们确定参数的类型和种类。

注: NumField().Field()只适用于结构。如果元素不是结构体,则会引起恐慌。格式说明符%T不能用于打印 Kind。这将打印reflect.Kind如果我们通过i.Kind()与一个printf语句%T ,它会打印reflect.Kind这实质上是在围棋各种的类型。

值得注意的是, reflect.Value value.Field(i)的类型是reflect.Value ,它是Type 而不是Kind。种类显示在以下行中。因此,我们在 Go Lang 中看到了反射的重要性和功能。在运行时了解变量的类型使我们能够编写大量通用代码。因此反射是 Golang 不可或缺的基础