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

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

Apr 17, 2020 pm 02:30 PM
golang 反射

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 サイトの他の関連記事を参照してください。

このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

Golang を使用してファイルを安全に読み書きするにはどうすればよいですか? Golang を使用してファイルを安全に読み書きするにはどうすればよいですか? Jun 06, 2024 pm 05:14 PM

Go ではファイルを安全に読み書きすることが重要です。ガイドラインには以下が含まれます。 ファイル権限の確認 遅延を使用してファイルを閉じる ファイル パスの検証 コンテキスト タイムアウトの使用 これらのガイドラインに従うことで、データのセキュリティとアプリケーションの堅牢性が確保されます。

Golang データベース接続用の接続プールを構成するにはどうすればよいですか? Golang データベース接続用の接続プールを構成するにはどうすればよいですか? Jun 06, 2024 am 11:21 AM

Go データベース接続の接続プーリングを構成するにはどうすればよいですか?データベース接続を作成するには、database/sql パッケージの DB タイプを使用します。同時接続の最大数を制御するには、MaxOpenConns を設定します。アイドル状態の接続の最大数を設定するには、ConnMaxLifetime を設定します。

golangフレームワークの長所と短所の比較 golangフレームワークの長所と短所の比較 Jun 05, 2024 pm 09:32 PM

Go フレームワークは、その高いパフォーマンスと同時実行性の利点で際立っていますが、比較的新しい、開発者エコシステムが小さい、一部の機能が欠けているなどの欠点もあります。さらに、急速な変化と学習曲線はフレームワークごとに異なる場合があります。 Gin フレームワークは、効率的なルーティング、組み込みの JSON サポート、強力なエラー処理機能により、RESTful API を構築するための一般的な選択肢です。

Golang フレームワークと Go フレームワーク: 内部アーキテクチャと外部機能の比較 Golang フレームワークと Go フレームワーク: 内部アーキテクチャと外部機能の比較 Jun 06, 2024 pm 12:37 PM

GoLang フレームワークと Go フレームワークの違いは、内部アーキテクチャと外部機能に反映されています。 GoLang フレームワークは Go 標準ライブラリに基づいてその機能を拡張していますが、Go フレームワークは特定の目的を達成するための独立したライブラリで構成されています。 GoLang フレームワークはより柔軟であり、Go フレームワークは使いやすいです。 GoLang フレームワークはパフォーマンスの点でわずかに優れており、Go フレームワークはよりスケーラブルです。ケース: gin-gonic (Go フレームワーク) は REST API の構築に使用され、Echo (GoLang フレームワーク) は Web アプリケーションの構築に使用されます。

Golang フレームワークでのエラー処理のベスト プラクティスは何ですか? Golang フレームワークでのエラー処理のベスト プラクティスは何ですか? Jun 05, 2024 pm 10:39 PM

ベスト プラクティス: 明確に定義されたエラー タイプ (エラー パッケージ) を使用してカスタム エラーを作成する 詳細を提供する エラーを適切にログに記録する エラーを正しく伝播し、非表示または抑制しないようにする コンテキストを追加するために必要に応じてエラーをラップする

GolangでJSONデータをデータベースに保存するにはどうすればよいですか? GolangでJSONデータをデータベースに保存するにはどうすればよいですか? Jun 06, 2024 am 11:24 AM

JSON データは、gjson ライブラリまたは json.Unmarshal 関数を使用して MySQL データベースに保存できます。 gjson ライブラリは、JSON フィールドを解析するための便利なメソッドを提供します。json.Unmarshal 関数には、JSON データをアンマーシャリングするためのターゲット型ポインターが必要です。どちらの方法でも、SQL ステートメントを準備し、データをデータベースに永続化するために挿入操作を実行する必要があります。

golang フレームワークでよくあるセキュリティ問題を解決するにはどうすればよいですか? golang フレームワークでよくあるセキュリティ問題を解決するにはどうすればよいですか? Jun 05, 2024 pm 10:38 PM

Go フレームワークで一般的なセキュリティ問題に対処する方法 Web 開発で Go フレームワークが広く採用されているため、そのセキュリティを確保することが重要です。以下は、一般的なセキュリティ問題を解決するための実践的なガイドであり、サンプル コードも含まれています。 1. SQL インジェクション SQL インジェクション攻撃を防ぐには、プリペアド ステートメントまたはパラメータ化されたクエリを使用します。例: constquery="SELECT*FROMusersWHEREusername=?"stmt,err:=db.Prepare(query)iferr!=nil{//Handleerror}err=stmt.QueryR

Golang の正規表現に一致する最初の部分文字列を見つけるにはどうすればよいですか? Golang の正規表現に一致する最初の部分文字列を見つけるにはどうすればよいですか? Jun 06, 2024 am 10:51 AM

FindStringSubmatch 関数は、正規表現に一致する最初の部分文字列を検索します。この関数は、最初の要素が一致した文字列全体で、後続の要素が個々の部分文字列である、一致する部分文字列を含むスライスを返します。コード例: regexp.FindStringSubmatch(text,pattern) は、一致する部分文字列のスライスを返します。実際のケース: 電子メール アドレスのドメイン名を照合するために使用できます。たとえば、email:="user@example.com", pattern:=@([^\s]+)$ を使用してドメイン名を照合します。 [1]。

See all articles