この記事では主に Golang でのリフレクションについて説明し、皆さんについて新たな理解を得ることができれば幸いです。
Go 言語を一定期間使用している人は多く、中には 1 ~ 2 年使用している人もいますが、反映についてはまだ曖昧です。 Go言語. 、私は自分の心にあまり自信がありません。 [関連する推奨事項: Go ビデオ チュートリアル 、プログラミング教育 ]
さらに、リフレクションはほとんど使用されません。もちろん、それ自体に問題はありません。当然のことながら、最もシンプルで、最も効率的で、スケーラブルで、パフォーマンスの良い方法で処理するのが最も良い方法です。高度な使用法を機械的にコピーする必要はありません。結局のところ、仕事は私たちの実験場ではありません。実際に行って実験することができます。自分で、 this リフレクションの遊び方を詳しく見てみましょう
この記事では次の 5 つの側面から説明します
これは Go 言語で提供されるメカニズムです。言語のパブリック ライブラリでの Reflect の使用については多くのことがわかります。
たとえば、一般的に使用される fmt パッケージでは、一般的に使用される
json シリアル化と逆シリアル化、当然、先ほど述べた gorm ライブラリもリフレクションを使用します
しかし、なぜ一般的に「リフレクションはどうですか?」を使用するのでしょうか?
リフレクションの機能によると、当然のことですが、提供するインターフェイスでは受信データ型がわからないため、特定のデータ型はプログラムの実行中にのみわかります
しかし、コーディング時には、プログラムの実行時に渡された型が何であるかを検証し (json のシリアル化など)、この特定のデータを操作したいとも考えています。このとき、リフレクションの機能を使用する必要がありますリフレクションが使用されている場所では、インターフェイス{}
が表示されるのは驚くことではありませんか?受信データ型が何になるかわからないからこそ、インターフェースとして設計しました。{}。インターフェースの特徴や使い方がわからない場合は、過去の記事を確認してください。 . :
インターフェースについて注意すべき点は何ですか?{} #インターフェースについて注意すべき点は何ですか?{}次##反省することができますインターフェイスの変更に使用されます。 Type 変数はリフレクション タイプのオブジェクトに変換されます。
まったく同等そのため、ベストを変更することで、さまざまな操作を行うことができます。
ユースケースに注意して柔軟に使用してください リフレクション タイプ オブジェクトは、ここでは、reflect リフレクション パッケージの reflect.Type
および reflect.Value
オブジェクトとして理解できます。これらは、で提供される TypeOf# を通じて使用できます。 ## および ValueOf 関数は
reflect.Type は実際にはインターフェースであり、実装する必要があるさまざまなインターフェースが含まれています)。 Type 関連情報
reflect.Type のすべてのメソッドが表示されます。その中には、
reflect.Value
実際 上記は構造体です。この構造体に従って、データ型と特定のデータを格納するメソッドのセットも関連付けられています。データ構造を見るとわかりますtype Value struct { typ *rtype ptr unsafe.Pointer flag }
参照unsafe.Pointer はこちら ご存知ですか? 最下位層は自然に
unsafe.Pointer を uintptr に変換し、そのデータを変更してから元に戻すことができます。 、次の記事を確認できます:
func main() { var demoStr string = "now reflect" fmt.Println("type:", reflect.TypeOf(demoStr)) fmt.Println("value:", reflect.ValueOf(demoStr)) }
法律の場合 2 番目、リフレクション型オブジェクトをインターフェイス型変数に変換します
型を特定のデータ型に変換できます。対応する # があります。 ##typ *rtype および
ptr unsafe.Pointer in Value
. たとえば、reflect.Value
オブジェクトのインターフェイスを渡すことができます。 () に対処するメソッド
#
func main() { var demoStr string = "now reflect" fmt.Println("type:", reflect.TypeOf(demoStr)) fmt.Println("value:", reflect.ValueOf(demoStr)) var res string res = reflect.ValueOf(demoStr).Interface().(string) fmt.Println("res == ",res) }法則 3 については、リフレクション タイプのオブジェクトを変更します
最初に、デモ コードでは、
TypeOf で渡される変数は実際にはコピーです。リフレクション型オブジェクトでその値を変更したい場合は、そのアドレスを取得する必要があります。 この変数は書き込み可能であることが前提です 例を挙げると理解できます func main() {
var demoStr string = "now reflect"
v := reflect.ValueOf(demoStr)
fmt.Println("is canset ", v.CanSet())
//v.SetString("hello world") // 会panic
}
reflect.Value オブジェクトの
CanSet を呼び出して、書き込み可能かどうかを確認できます。書き込み可能であれば、再度書き込みます。書き込み可能でない場合は、書き込みを行わないでください。そうしないとパニックになります
reflect.Value で特定のデータ ポインタを見つける必要があります。そうすれば、それを変更できます。
reflect.Value的
Elemメソッドもう少し複雑なもの
上記のケースを見てみればそう感じるかもしれません 簡単なケースはデモするまでは大丈夫ですが、仕事で使うとすぐにクラッシュしてしまいます 当然まだ完全には理解されていません。うまく消化されていません。これは仕事からの別の例です。
sport
##1type RDemo struct { Name string Age int Money float32 Hobby map[string][]string } func main() { tmp := &RDemo{ Name: "xiaomiong", Age: 18, Money: 25.6, Hobby: map[string][]string{ "sport": {"basketball", "football"}, "food": {"beef"}, }, } v := reflect.ValueOf(tmp).Elem() // 拿到结构体对象 h := v.FieldByName("Hobby") // 拿到 Hobby 对象 h1 := h.MapKeys()[0] // 拿到 Hobby 的第 0 个key fmt.Println("key1 name == ",h1.Interface().(string)) sli := h.MapIndex(h1) // 拿到 Hobby 的第 0 个key对应的对象 str := sli.Index(1) // 拿到切片的第 1 个对象 fmt.Println(str.CanSet()) str.SetString("helloworld") fmt.Println("tmp == ",tmp) }
可以看到上述案例运行之后有时可以运行成功,有时会出现 panic 的情况,相信细心的 xdm 就可以看出来,是因为 map 中的 key 是 无序的导致的,此处也提醒一波,使用 map 的时候要注意这一点
看上述代码,是不是就能够明白咱们使用反射去找到对应的数据类型,然后按照数据类型进行处理数据的过程了呢
有需要的话,可以慢慢的去熟练反射包中涉及的函数,重点是要了解其三个规则,对象转换方式,访问方式,以及数据修改方式
那么通过上述案例,可以知道关于反射中数据类型和数据指针对应的值是相当重要的,不同的数据类型能够用哪些函数这个需要注意,否则用错直接就会 panic
来看 TypeOf 的接口中涉及的数据结构
在 reflect 包中 rtype
是非常重要的,Go 中所有的类型都会包含这个结构,所以咱们反射可以应用起来,结构如下
// rtype must be kept in sync with ../runtime/type.go:/^type._type. type rtype struct { size uintptr ptrdata uintptr hash uint32 tflag tflag align uint8 fieldAlign uint8 kind uint8 equal func(unsafe.Pointer, unsafe.Pointer) bool gcdata *byte str nameOff ptrToThis typeOff }
其中可以看到此处的 rtype
的结构保持和 runtime/type.go
一致 ,都是关于数据类型的表示,以及对应的指针,关于这一块的说明和演示可以查看文末的 interface{} 处的内容
从 ValueOf 的源码中,我们可以看到,重要的是 emptyInterface 结构
// emptyInterface is the header for an interface{} value.type emptyInterface struct { typ *rtype word unsafe.Pointer }复制代码
emptyInterface 结构中有 rtype
类型的指针, word 自然是对应的数据的地址了
reflect.Value
对象中的方法也是非常的多,用起来和上述说到的 reflect.Type
接口中的功能类似
关于源码中涉及到的方法,就不再过多的赘述了,更多的还是需要自己多多实践才能体会的更好
殊不知,此处的 reflect.Value
也是可以转换成 reflect.Type
,可以查看源码中 reflect\value.go
的 func (v Value) Type() Type {
其中 reflect.Value
,reflect.Type
,和任意数据类型
可以相互这样来转换
如下图:
至此,关于反射就聊到这里,一些关于源码的细节并没有详细说,更多的站在一个使用者的角度去看反射需要注意的点
关于反射,大多的人是建议少用,因为是会影响到性能,不过如果不太关注这一点,那么用起来还是非常方便的
高级功能自然也是双刃剑,你用不好就会 panic,如果你期望去使用他,那么就去更多的深入了解和一步一步的吃透他吧
大道至简,反射三定律,活学活用
更多编程相关知识,请访问:编程视频!!
以上がGolangのリフレクションを詳しく解説した記事の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。