1. ホーム
  2. スクリプト・コラム
  3. ゴラン

Golang言語の学習は、Goの反射の例のチュートリアルをピンダウンする

2022-02-13 18:59:56

1. リフレクション入門

1.1 リフレクションとは?

Go 言語は、変数やそれを呼び出すメソッド、それらがサポートする固有操作の値を実行時に更新・確認する仕組みを提供していますが、コンパイル時にはその内容を知ることなく specific types この機構は Reflection . Reflectionでは、型そのものを第一級の値型として扱うこともできます。

リフレクションとは、実行時にプログラムそのものにアクセスし、修正する機能である。プログラムは変数がメモリアドレスに変換されてコンパイルされ、変数名はコンパイラによって実行ファイルに書き込まれず、プログラム実行時に自分自身の情報にアクセスすることはできない。

例として
通常、変数を直交するように定義します。

var a int


変数aをint型として定義する

今、私は変数aが何型であるかを知らないが、リフレクションによって変数aが何型であるかも知ることができるのだ! それは何型なのか!

type FanOne struct {
	name string
name string }
func main(){	
	var a int = 1
	var d FanOne
	fmt.Println(reflect.TypeOf(a)) // int 
	// Here we get the type of a! Note the type! Not the category! Although this type and category are the same
	// The difference between Type and Kind will be explained later
	fmt.Println(reflect.ValueOf(a).Kind()) //int 
	//This gets the category of a, which is determined by the value of a
	fmt.Println(reflect.TypeOf(d)) //main.FanOne
	// type is main.FanOne is defined inside main FanOne
	fmt.Println(reflect.ValueOf(d).Kind()) //struct
	// The category is struct
	// output the type name and kind of d. The type name is FanOne
	// and FanOne belongs to a class of structures, so the class is struct
}


このカテゴリーとタイプは同じ時もあれば、違う時もあるわけです。

1.2 なぜリフレクションが必要なのか?

開発では、我々は関数の値を処理するときに、しかし、関数は、値のより多くの型を受け入れることができることを確認するために、インターフェイスは、すべてのデータ型を受け入れることができますが、強い型付け言語であるため、データの処理では、別の処理の異なるタイプに非常に冗長なコードに見えるでしょう、我々はに反射を使用できます 我々は決定し、受信パラメータを処理するために反射を使用することができます。

詳しくはサンプルをご覧ください。

2.リフレクトパッケージ

2.1 基本的なリフレクション

reflect.TypeOf() //Get the type of the variable, returning reflect.
reflect.ValueOf() //Gets the value of the variable, returning the type reflect.
Kind() //Get the class of the variable, return a constant
reflect.Value.Interface() //convert to interface{} type


2.2 反射とポインタ

Go言語のプログラムでポインタにリフレクションオブジェクトを取得する場合、そのポインタに対して reflect.Elem() メソッドを使用して、このポインタが指す要素の型を取得します。この取得処理を要素のフェッチと呼びます。 * 操作

reflect.ValueOf(xxx).Elem()


2.3 反射とオブジェクト

リフレクションは reflect.new(xxx) または reflect.zero(xxx) を使用して、リフレクションを行い、元のタイプのオブジェクトを作成します。

func CreatePrimitiveObjects(t reflect.Type) reflect.Value {
    return reflect.Zero(t)
}


を使用することも可能です。

reflect.New()


を実行すると、元のオブジェクトの作成が行われます。

2.4 反射と機能

もし反射値オブジェクト (reflect.Value) の値が関数型である場合、その関数に reflect.Value 関数が呼び出されます。リフレクションを使って関数を呼び出す場合、引数にはリフレクションされた値オブジェクトのスライスを使用する必要があります []reflect.Value に構築され、渡される。 Call() メソッドを呼び、呼び出しが完了すると、関数の戻り値が []reflect.Value が返されます。
リフレクションでは、関数とメソッドの両方の型(Type)はreflect.Func.となります。関数を呼び出したい場合は、例えばValueのCall()メソッドで呼び出すことができます。

package main

import (
	"fmt"
	"reflect"
)

func FanOne() string {
	return "One-click triple"
}

func FanOneWoW(a string) string {
	return fmt.Sprintf("%s to give FanOne a one-button triplet~",a)
}

func main() {
	FanOneNotArgs := reflect.ValueOf(FanOne).Call([]reflect.Value{}) //no arguments
	FanOneHaveArgs := reflect.ValueOf(FanOneWoW).Call([]reflect.Value{reflect.ValueOf("I")}) //with arguments
	fmt.Println(FanOneNotArgs[0])
	fmt.Println(FanOneHaveArgs[0])
}



2.5 反映例

出力が次のようになるように、fn関数を埋めてください。

switch、if、その他の select 文を使用しないことが必要です。

func fn(callback interface{}, bytes []byte) {
		//coding
}
type aaa struct {
	Name string `json:"name"`
	Age int `json:"age"`
}
func Test(t *testing.T) {
		fn(func(a []*aaa) string {
		aaas := a
		for i, item := range aaas {
			fmt.Println(i, item)
		}
		fmt.Println("12312312, ", aaas)
		return "xxxx"
	}, []byte("[{\"name\":\"111\",\"age\":1}, {\"name\":\"gsjk\",\"age\" ;:2}]"))

	fn(func(a []aaa) string {
		aaas := a
		for i, item := range aaas {
			fmt.Println(i, item)
		}

		fmt.Println("12312312, ", aaas[0])
		return "xxxx"
	}, []byte("[{\"name\":\"111\",\"age\":1}, {\"name\":\"gsjk\",\"age\" ;:2}]"))

	fn(func(a * aaa) string {
		fmt.Println("12312312, ", a)
		aaas := a
		fmt.Println("12312312, ", aaas)
		return "xxxx"
	}, []byte("{\"name\":\"gsjk\",\"age\":2}"))

	fn(func(a string) string {
		fmt.Println("12312312, ", a)
		aaas := a
		fmt.Println("12312312, ", aaas)
		return "xxxx"
	}, []byte("\"ssss\""))

	fn(func(a int) string {
		fmt.Println("-----------, ", a)
		aaas := a
		fmt.Println("-----------, ", aaas)
		return "xxxx"
	}, []byte("123"))
}


(1)まず、テストに関する知識です。
名前に_testがないとエラーになるようで、そのようにしました。

go test xxx_test.go
go test -v xxx_test.go


(2) 次に、このfn()内の無名関数を理解することです。
別途に取り出してください

func(a []*aaa) string {
		aaas := a
		for i, item := range aaas {
			fmt.Println(i, item)
		}
		fmt.Println("12312312, ", aaas)
		return "xxxx"
	}, []byte("[{\"name\":\"111\",\"age\":1}, {\"name\":\"gsjk\",\"age\" ;:2}]"))


これは、*aaa型の配列であることがわかります。したがって、我々の仕事は、関数fnの中に無名関数を反映させ、この反映された無名関数を呼び出して引数を渡すことである。

例として、最初の1枚をご紹介します。

(3) では、まずインターフェイス{}の ValueOf と TypeOf から、この無名関数の様々な値を見てみましょう。

func fn(callback interface{}, bytes []byte) {
	v := reflect.ValueOf(callback) //0xbaff40
	t := reflect.TypeOf(callback) //func([]*main.aaa) string
}


参照される関数のTypeが func([]*main.aaa) string ということは

paramsValue := t.In(0) //[]*main.aaa


匿名関数の入力パラメータを取得する

(4) フォーカス!!!
私たちはこれを得た paramsValue はちょうど []*main.aaa という名前の場合、この値は reflect.type タイプ!!! は、その
私たちが欲しいのは []*main.aaa 欲しいのは名前ではなく型だ!
そのため、このタイプのオブジェクトを作成し、適切なタイプに変換する必要があります。

	val := reflect.New(paramsValue)
	newT := val.Interface()
	fmt.Printf("valValue:%v , valType: %T \n",val,val) //valValue:&[] , valType: reflect.Value
	fmt.Printf("newTValue:%v , newTType: %T \n",newT,newT)//newTValue:&[] , newTType: *[]*main.aaa


このようなオブジェクトのクラスを作りたいのです。goはオブジェクト指向プログラミングではありませんが、ここではそのように理解することができます。

なぜこの型が必要なのか?

なぜなら、バイトスライスは後でこの型の変数にデシリアライズされ、この無名関数に渡されるからです!

	if v.IsValid() { //function valid or not
		_ = json.Unmarshal(bytes, newT) //byte to json
	}


そこでまたまた問題なのですが、渡された値の型が []*main.aaa しかし、私たちは *[]*main.aaa この型は、明らかに間違っている。

	fmt.Printf("Call callback end callback ret = %s \n", v.Call([]reflect.Value{reflect.ValueOf(newT)}))
	fmt.Printf("*************************\n")


エラーが報告されました! 型が違います!

次に * という操作を行います。リフレクションでは * を取り除くことができます! 以下は、リフレクションでは動作しません。

package main

import (
  "fmt"
  "reflect"
)

func main(){
  var a int = 1
  var b *int = &a
  var c **int = &b
  fmt.Println(a, *b, c)
  fmt.Println(reflect.TypeOf(a))
  fmt.Println(reflect.TypeOf(*b))
  fmt.Println(reflect.TypeOf(b))
}


次に reflect.Elem() を削除して、この

*
	fmt.Printf("Call callback end callback ret = %s \n", v.Call([]reflect.Value{reflect.ValueOf(newT).Elem()}))
	fmt.Printf("*************************\n")

if

素晴らしい仕事です。

3. 概要

私は非常に少ない反射を使用するために使用される、基本的にプロジェクトで使用しませんでしたが、夏のインターンシップは、最初のタスクは、この知識を補うために狂気の反射インターフェイスを記述することです、私のための反射、本当に少し理解するのは難しい、それは私がそれを行うには2日かかりました。

元々私のアイディアは assert を使用して型を決定するか、あるいは switch を使用し、その後に switch しかし、これではあまり実用的ではありませんし、名前を変えたらコードも変えなければなりません。例えば、構造体の名前をbbbに変更すると、うまくいきません。

あとは、型をオブジェクトとして反映させ、それをオブジェクトに代入すれば、より柔軟に対応できますね

学習した!

インターンシップは苦しかった! でも、新しいことをたくさん学べた! そして、たくさんの素晴らしい仲間を導くことができました! そしてお給料! そして快適さも!

上記は、Go反射例チュートリアルの詳細をピンチするGolang言語の学習は、Go反射チュートリアルについての詳細な情報は、スクリプトホーム他の関連記事に従ってくださいです