ホームページ > バックエンド開発 > Golang > golang リフレクションの用途は何ですか?

golang リフレクションの用途は何ですか?

青灯夜游
リリース: 2020-04-17 14:30:36
オリジナル
2897 人が閲覧しました

golang リフレクションは何に役立ちますか?次の記事では、golang リフレクションについて紹介します。一定の参考値があるので、困っている友達が参考になれば幸いです。

golang リフレクションの用途は何ですか?

#golang (go) は、高速なマシンコードのコンパイルに使用できる手続き型プログラミング言語です。静的に型付けされたコンパイル言語です。マルチコアおよびネットワーク化されたマシンレベルのプログラムの開発を容易にする同時実行メカニズムを提供します。これは、高速で動的に型付けおよび解釈される言語であり、インターフェイスと型の埋め込みのサポートを提供します。

基本的な理解

Go 言語では、ほとんどの場合、値、型、関数は非常に単純です。必要に応じて定義します。 。 Struct が必要です

type Foo struct {
    A int 
    B string
}
ログイン後にコピー

値が必要なので、それを定義します

var x Foo
ログイン後にコピー

関数が必要なので、それを定義します

func DoSomething(f Foo) {
  fmt.Println(f.A, f.B)
}
ログイン後にコピー

しかし、場合によっては、できることを実行する必要があります。ファイルまたはネットワークから辞書データを取得するなど、実行時にのみ決定されます。あるいは、いくつかの異なる種類のデータを取得したい場合もあります。この場合、reflection が役に立ちます。 Reflection を使用すると、次の機能を使用できます:

  • 実行時の型の確認

  • 実行時の確認/変更/作成値/関数/構造

一般に、Go の reflection は 3 つの概念 TypesKinds、## を中心に展開します。 #値。リフレクションに関するすべての操作は、reflect パッケージ

Reflection の Power

Type の Power# にあります。 ##まず、リフレクションを通じて値の型を取得する方法を見てみましょう。

varType := reflect.TypeOf(var)
ログイン後にコピー

リフレクション インターフェイスから、使用を待っている関数がたくさんあることがわかります。コメントを見ればわかります。リフレクション パッケージは、何をしようとしているのかわかっていることを前提としています。たとえば、varType.Elem() はパニックになります。なぜなら、Elem() には Array、Chan、Map、Ptr、または Slice に対してのみこのメソッドがあり、これらの型にはこのメソッドしかありません。詳細についてはテストコードを参照してください。次のコードを実行すると、すべてのリフレクト関数の例を確認できます。

package main
import (
    "fmt"
    "reflect"
)
type FooIF interface {
    DoSomething()
    DoSomethingWithArg(a string)
    DoSomethingWithUnCertenArg(a ... string)
}
type Foo struct {
    A int
    B string
    C struct {
        C1 int
    }
}
func (f *Foo) DoSomething() {
    fmt.Println(f.A, f.B)
}
func (f *Foo) DoSomethingWithArg(a string) {
    fmt.Println(f.A, f.B, a)
}
func (f *Foo) DoSomethingWithUnCertenArg(a ... string) {
    fmt.Println(f.A, f.B, a[0])
}
func (f *Foo) returnOneResult() int {
    return 2
}
func main() {
    var simpleObj Foo
    var pointer2obj = &simpleObj
    var simpleIntArray = [3]int{1, 2, 3}
    var simpleMap = map[string]string{
        "a": "b",
    }
    var simpleChan = make(chan int, 1)
    var x uint64
    var y uint32
    varType := reflect.TypeOf(simpleObj)
    varPointerType := reflect.TypeOf(pointer2obj)
    // 对齐之后要多少容量
    fmt.Println("Align: ", varType.Align())
    // 作为结构体的`field`要对其之后要多少容量
    fmt.Println("FieldAlign: ", varType.FieldAlign())
    // 叫啥
    fmt.Println("Name: ", varType.Name())
    // 绝对引入路径
    fmt.Println("PkgPath: ", varType.PkgPath())
    // 实际上用了多少内存
    fmt.Println("Size: ", varType.Size())
    // 到底啥类型的
    fmt.Println("Kind: ", varType.Kind())
    // 有多少函数
    fmt.Println("NumMethod: ", varPointerType.NumMethod())
    // 通过名字获取一个函数
    m, success := varPointerType.MethodByName("DoSomethingWithArg")
    if success {
        m.Func.Call([]reflect.Value{
            reflect.ValueOf(pointer2obj),
            reflect.ValueOf("sad"),
        })
    }
    // 通过索引获取函数
    m = varPointerType.Method(1)
    m.Func.Call([]reflect.Value{
        reflect.ValueOf(pointer2obj),
        reflect.ValueOf("sad2"),
    })
    // 是否实现了某个接口
    fmt.Println("Implements:", varPointerType.Implements(reflect.TypeOf((*FooIF)(nil)).Elem()))
    //  看看指针多少bit
    fmt.Println("Bits: ", reflect.TypeOf(x).Bits())
    // 查看array, chan, map, ptr, slice的元素类型
    fmt.Println("Elem: ", reflect.TypeOf(simpleIntArray).Elem().Kind())
    // 查看Array长度
    fmt.Println("Len: ", reflect.TypeOf(simpleIntArray).Len())
    // 查看结构体field
    fmt.Println("Field", varType.Field(1))
    // 查看结构体field
    fmt.Println("FieldByIndex", varType.FieldByIndex([]int{2, 0}))
    // 查看结构提field
    fi, success2 := varType.FieldByName("A")
    if success2 {
        fmt.Println("FieldByName", fi)
    }
    // 查看结构体field
    fi, success2 = varType.FieldByNameFunc(func(fieldName string) bool {
        return fieldName == "A"
    })
    if success2 {
        fmt.Println("FieldByName", fi)
    }
    //  查看结构体数量
    fmt.Println("NumField", varType.NumField())
    // 查看map的key类型
    fmt.Println("Key: ", reflect.TypeOf(simpleMap).Key().Name())
    // 查看函数有多少个参数
    fmt.Println("NumIn: ", reflect.TypeOf(pointer2obj.DoSomethingWithUnCertenArg).NumIn())
    // 查看函数参数的类型
    fmt.Println("In: ", reflect.TypeOf(pointer2obj.DoSomethingWithUnCertenArg).In(0))
    // 查看最后一个参数,是否解构了
    fmt.Println("IsVariadic: ", reflect.TypeOf(pointer2obj.DoSomethingWithUnCertenArg).IsVariadic())
    // 查看函数有多少输出
    fmt.Println("NumOut: ", reflect.TypeOf(pointer2obj.DoSomethingWithUnCertenArg).NumOut())
    // 查看函数输出的类型
    fmt.Println("Out: ", reflect.TypeOf(pointer2obj.returnOneResult).Out(0))
    // 查看通道的方向, 3双向。
    fmt.Println("ChanDir: ", int(reflect.TypeOf(simpleChan).ChanDir()))
    // 查看该类型是否可以比较。不能比较的slice, map, func
    fmt.Println("Comparable: ", varPointerType.Comparable())
    // 查看类型是否可以转化成另外一种类型
    fmt.Println("ConvertibleTo: ", varPointerType.ConvertibleTo(reflect.TypeOf("a")))
    // 该类型的值是否可以另外一个类型
    fmt.Println("AssignableTo: ", reflect.TypeOf(x).AssignableTo(reflect.TypeOf(y)))
}
ログイン後にコピー

Value's Power

変数の型を確認するだけでなく、読み取り/書き込み/反射値を通じて新しいものを作成します。ただし、変数の値を変更する場合は、まずリフレクション値 type

refVal := reflect.ValueOf(var)
ログイン後にコピー

を取得します。変数を指すリフレクションポインタを取得する必要がありますが、具体的な理由は後述します

refPtrVal := reflect.ValueOf(&var)
ログイン後にコピー

もちろんreflect.Valueがあるので、Type()メソッドでreflect.Typeを簡単に取得できます。変数の値を変更する場合は、

refPtrVal.Elem().Set(newRefValue)
ログイン後にコピー

を使用します。もちろん、Set メソッドのパラメーターも反映する必要があります。Value

新しい値を作成する場合は、次のコード

newPtrVal := reflect.New(varType)
ログイン後にコピー

次に、Elem().Set()を使用して値を初期化します。もちろん、さまざまな値に対してさまざまな方法がたくさんあります。ここには書きません。次の公式の例に焦点を当てましょう

package main
import (
    "fmt"
    "reflect"
)
func main() {
    // swap is the implementation passed to MakeFunc.
    // It must work in terms of reflect.Values so that it is possible
    // to write code without knowing beforehand what the types
    // will be.
    swap := func(in []reflect.Value) []reflect.Value {
        return []reflect.Value{in[1], in[0]}
    }
    // makeSwap expects fptr to be a pointer to a nil function.
    // It sets that pointer to a new function created with MakeFunc.
    // When the function is invoked, reflect turns the arguments
    // into Values, calls swap, and then turns swap's result slice
    // into the values returned by the new function.
    makeSwap := func(fptr interface{}) {
        // fptr is a pointer to a function.
        // Obtain the function value itself (likely nil) as a reflect.Value
        // so that we can query its type and then set the value.
        fn := reflect.ValueOf(fptr).Elem()
        // Make a function of the right type.
        v := reflect.MakeFunc(fn.Type(), swap)
        // Assign it to the value fn represents.
        fn.Set(v)
    }
    // Make and call a swap function for ints.
    var intSwap func(int, int) (int, int)
    makeSwap(&intSwap)
    fmt.Println(intSwap(0, 1))
    // Make and call a swap function for float64s.
    var floatSwap func(float64, float64) (float64, float64)
    makeSwap(&floatSwap)
    fmt.Println(floatSwap(2.72, 3.14))
}
ログイン後にコピー

#Principle

#Recognize type and Interface##Goは静的型付け言語です。各変数には int、float などの静的型があります。静的型とは何ですか? 私の理解では、一定の長さのバイナリ ブロックとその説明です。たとえば、同じバイナリ ブロック 00000001 は、bool 型では true を意味します。 int 型での説明は 1 です。次の最も単純な例を見てみましょう。

type MyInt int
var i int
var j MyInt
ログイン後にコピー

i,j はメモリ内では基になる int 型で表されますが、実際のコーディング プロセスでは、それらは int 型ではありません。コンパイル時に同じ型になるため、i の値を j に直接代入することはできません。これは少し奇妙ではありませんか? これを実行すると、コンパイラは、MyInt 型の値を int 型の値に代入できないことを通知します。この型はクラスでも Python 型でもありません。Interface はメソッドのコレクションを表す特別な型です。インターフェイス値は、その値がインターフェイス メソッドを実装している限り、任意の特定の値を格納できます。 Interface{} は Java のオブジェクトに似ている場合があります。実際、interface は、実際の値と値の特定の型の 2 つの部分で構成されます。これは、次のコードが他の言語と異なる理由も説明できます。インターフェースの原理の詳細については、go データ構造: インターフェースを参照してください。

package main
import (
    "fmt"
)
type A interface {
    x(param int)
}
type B interface {
    y(param int)
}
type AB struct {
}
func (ab *AB) x(param int) {
    fmt.Printf("%p", ab)
    fmt.Println(param)
}
func (ab *AB) y(param int) {
    fmt.Printf("%p", ab)
    fmt.Println(param)
}
func printX(a A){
    fmt.Printf("%p", a)
    a.x(2)
}
func printY(b B){
    fmt.Printf("%p", b)
    b.y(3)
}
func main() {
    var ab = new(AB)
    printX(ab)
    printY(ab)
    var aInfImpl A
    var bInfImpl B
    aInfImpl = new(AB)
    //bInfImpl = aInfImpl  会报错
    bInfImpl = aInfImpl.(B)
    bInfImpl.y(2)
}
ログイン後にコピー

Golang の 3 つのリフレクション定理

インターフェース値をリフレクション オブジェクトに分割するReflectionインターフェイス値 (値、タイプ) を確認するためにのみ使用されます。前の章で説明したように、ValueOf と TypeOf の 2 つのメソッドがあります。 ValueOf を使用すると、Type

package main
import (
    "fmt"
    "reflect"
)
func main() {
    var x float64 = 3.4
    fmt.Println("type:", reflect.TypeOf(x))
}
ログイン後にコピー

を簡単に取得できます。このコードは

type: float64
ログイン後にコピー
を出力します。その後、インターフェイスはどこにあるのかという疑問が生じます。 float64 変数を宣言するだけです。インターフェースはどこから来たのか。はい、答えは TypeOf パラメーターに隠されています

func TypeOf(i interface{}) Type
ログイン後にコピー

reflect.TypeOf(x) を呼び出すと、x は最初に空のインターフェイスに格納されます。その後、パラメータとして関数実行スタックに渡されます。 **reflect.TypeOf は、このインターフェイスのペアをアンラップし、型情報を回復します。 **

リフレクション オブジェクトをインターフェイス値に結合します

就像镜面反射一样,go的反射是可逆的。给我一个reflect.Value。我们能够恢复出一个interface的值。事实上,以下函数干的事情就是将Value和Type组狠起来塞到 interface里面去。所以我们可以

y := v.Interface().(float64) // y will have type float64.
fmt.Println(y)
ログイン後にコピー

接下来就是见证奇迹的时刻。fmt.Println和fmt.Printf的参数都是interface{}。我们真正都不需要将上面例子的y转化成明确的float64。我就可以去打印他比如

fmt.Println(v.Interface())
ログイン後にコピー

甚至我们的interface藏着的那个type是float64。我们可以直接用占位符来打印

fmt.Println("Value is %7.le\n", v.Interface())
ログイン後にコピー

再重复一边,没有必要将v.Interface()的类型强转到float64;这个空的interface{}包含了concrete type。函数调用会恢复出来

要改变一个反射对象,其值必须是可设置的

第三条比较让你比较困惑。不过如果我们理解了第一条,那么这条其实非常好理解。先看一下下面这个例子

var x float64 = 3.4
v := reflect.ValueOf(x)
v.SetFloat(7.1) // Error: will panic.
ログイン後にコピー

如果执行这段代码,你会发现出现panic以下信息

panic: reflect.Value.SetFloat using unaddressable value
ログイン後にコピー

可设置性是一个好东西,但不是所有reflect.Value都有他...可以通过CanSet 函数来获取是否可设置

var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("settability of v:", v.CanSet())
ログイン後にコピー

那么到底为什么要有一个可设置性呢?可寻址才可设置,我们在用reflect.ValueOf时候,实际上是函数传值。获取x的反射对象,实际上是另外一个float64的内存的反射对象。这个时候我们再去设置该反射对象的值,没有意义。这段内存并不是你申明的那个x。

推荐学习:Golang教程

以上がgolang リフレクションの用途は何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート