CanSet、CanAddrとは何ですか?
次のコラムでは、golang チュートリアル コラムの CanSet と CanAddr について紹介します。困っている友達の役に立つでしょう!
CanSet と CanAddr とは何かを理解するための記事?
setable (CanSet) とは
まず、settable がreflect.Value 用であることを明確にする必要があります。通常の変数をreflect.Valueに変換するには、最初にreflect.ValueOf()を使用して変換する必要があります。
では、なぜそのような「設定可能な」メソッドがあるのでしょうか?たとえば、次の例:
var x float64 = 3.4v := reflect.ValueOf(x)fmt.Println(v.CanSet()) // false
golang のすべての関数呼び出しは値のコピーであるため、reflect.ValueOf を呼び出すと、x がコピーされて渡され、ここで取得される v は x になります。コピーの価値。したがって、現時点では、v を通じて x 変数をここに設定できるかどうかを知りたいと考えています。これを行うには、CanSet()
というメソッドが必要です。ただし、x のコピーを渡しているため、ここでは x の値をまったく変更できないことは明らかです。ここに示されているものは虚偽です。
それでは、x のアドレスを渡すとどうなるでしょうか?以下に例を示します。
var x float64 = 3.4v := reflect.ValueOf(&x)fmt.Println(v.CanSet()) // false
x 変数のアドレスをreflect.ValueOfに渡します。 CanSet である必要があります。ただし、ここで注意すべき点は、ここでの v は x のポインタを指しているということです。したがって、CanSet メソッドは、x のポインターを設定できるかどうかを判断します。ポインターは絶対に設定できないため、ここでも false が返されます。
次に、このポインタの値から判断する必要があるのは、このポインタが指す要素を設定できるかどうかです。幸いなことに、reflect には「ポインタが指す要素」を取得するための Elem() メソッドが用意されています。 」。
var x float64 = 3.4v := reflect.ValueOf(&x)fmt.Println(v.Elem().CanSet()) // true
最終的に true を返します。ただし、Elem()を使用する場合は前提条件があり、ここでの値はポインタオブジェクトから変換されたreflect.Valueである必要があります。 (または、インターフェイス オブジェクト変換の場合は、reflect.Value)。この前提を理解するのは難しくありませんが、それが int 型の場合、どのようにしてそれが指す要素を持つことができるのでしょうか?したがって、この前提条件が満たされていない場合、Elem は直接パニックを引き起こすため、Elem を使用するときは十分に注意してください。
設定可能かどうかを判断した後、SetXX 一連のメソッドを通じて対応する設定を行うことができます。
var x float64 = 3.4v := reflect.ValueOf(&x)if v.Elem().CanSet() { v.Elem().SetFloat(7.1)}fmt.Println(x)
より複雑な型
複雑なスライス、マップ、構造体、ポインター、その他のメソッドについては、次の例を書きました。
package mainimport ( "fmt" "reflect")type Foo interface { Name() string}type FooStruct struct { A string}func (f FooStruct) Name() string { return f.A}type FooPointer struct { A string}func (f *FooPointer) Name() string { return f.A}func main() { { // slice a := []int{1, 2, 3} val := reflect.ValueOf(&a) val.Elem().SetLen(2) val.Elem().Index(0).SetInt(4) fmt.Println(a) // [4,2] } { // map a := map[int]string{ 1: "foo1", 2: "foo2", } val := reflect.ValueOf(&a) key3 := reflect.ValueOf(3) val3 := reflect.ValueOf("foo3") val.Elem().SetMapIndex(key3, val3) fmt.Println(val) // &map[1:foo1 2:foo2 3:foo3] } { // map a := map[int]string{ 1: "foo1", 2: "foo2", } val := reflect.ValueOf(a) key3 := reflect.ValueOf(3) val3 := reflect.ValueOf("foo3") val.SetMapIndex(key3, val3) fmt.Println(val) // &map[1:foo1 2:foo2 3:foo3] } { // struct a := FooStruct{} val := reflect.ValueOf(&a) val.Elem().FieldByName("A").SetString("foo2") fmt.Println(a) // {foo2} } { // pointer a := &FooPointer{} val := reflect.ValueOf(a) val.Elem().FieldByName("A").SetString("foo2") fmt.Println(a) //&{foo2} }}
上記の例を理解できれば、CanSet メソッドを基本的に理解できます。
マップとポインターが変更されるときに、reflect.ValueOf にポインターを渡す必要がないという事実に特に注意してください。なぜなら、それらはそれ自体がポインタだからです。
したがって、reflect.ValueOf を呼び出すときは、渡したい変数の基礎となる構造を明確にしておく必要があります。たとえば、map は実際にはポインターを転送するため、それを指す必要はもうありません。スライスに関しては、実際に渡されるのはSliceHeader構造体であり、Sliceを変更する際にはSliceHeaderのポインタを渡す必要があります。これは私たちがしばしば注意を払う必要があることです。
CanAddr
reflect パッケージには、CanSet に加えて CanAddr メソッドがあることがわかります。両者の違いは何ですか?
CanAddr メソッドと CanSet メソッドの違いは、構造体の一部のプライベート フィールドについては、アドレスを取得できますが、設定できないことです。
たとえば、次の例:
package mainimport ( "fmt" "reflect")type FooStruct struct { A string b int}func main() { { // struct a := FooStruct{} val := reflect.ValueOf(&a) fmt.Println(val.Elem().FieldByName("b").CanSet()) // false fmt.Println(val.Elem().FieldByName("b").CanAddr()) // true }}
つまり、CanAddr は CanSet の必要条件であり、不十分条件です。 CanAddr の場合は値。必ずしも CanSet である必要はありません。ただし、変数 canSet の場合は、CanAddr でなければなりません。
ソースコード
この値要素 CanSet または CanAddr を実装すると仮定すると、マーク ビット マークを使用する可能性が高くなります。まさにその通りです。
最初に Value の構造を見てみましょう:
type Value struct { typ *rtype ptr unsafe.Pointer flag}
ここで注意すべき点は、これは内部にフラグがネストされたネスト構造であり、フラグ自体は uintptr であるということです。 。
type flag uintptr
このフラグは非常に重要で、値の型だけでなく、いくつかのメタ情報 (アドレス指定可能かどうかなど) も表現できます。 flag は uint 型ですが、ビットマークで表されます。
まず、型を表す必要があります。golang には 27 個の型があります:
const ( Invalid Kind = iota Bool Int Int8 Int16 Int32 Int64 Uint Uint8 Uint16 Uint32 Uint64 Uintptr Float32 Float64 Complex64 Complex128 Array Chan Func Interface Map Ptr Slice String Struct UnsafePointer)
所以使用5位(2^5-1=63)就足够放这么多类型了。所以 flag 的低5位是结构类型。
第六位 flagStickyRO: 标记是否是结构体内部私有属性
第七位 flagEmbedR0: 标记是否是嵌套结构体内部私有属性
第八位 flagIndir: 标记 value 的ptr是否是保存了一个指针
第九位 flagAddr: 标记这个 value 是否可寻址
第十位 flagMethod: 标记 value 是个匿名函数
其中比较不好理解的就是 flagStickyRO,flagEmbedR0
看下面这个例子:
type FooStruct struct { A string b int}type BarStruct struct { FooStruct}{ b := BarStruct{} val := reflect.ValueOf(&b) c := val.Elem().FieldByName("b") fmt.Println(c.CanAddr())}
这个例子中的 c 的 flagEmbedR0 标记位就是1了。
所以我们再回去看 CanSet 和 CanAddr 方法
func (v Value) CanAddr() bool { return v.flag&flagAddr != 0}func (v Value) CanSet() bool { return v.flag&(flagAddr|flagRO) == flagAddr}
他们的方法就是把 value 的 flag 和 flagAddr 或者 flagRO (flagStickyRO,flagEmbedR0) 做“与”操作。
而他们的区别就是是否判断 flagRO 的两个位。所以他们的不同换句话说就是“判断这个 Value 是否是私有属性”,私有属性是只读的。不能Set。
应用
在开发 collection (https://github.com/jianfengye/collection)库的过程中,我就用到这么一个方法。我希望设计一个方法 func (arr *ObjPointCollection) ToObjs(objs interface{}) error
,这个方法能将 ObjPointCollection 中的 objs reflect.Value 设置为参数 objs 中。
func (arr *ObjPointCollection) ToObjs(objs interface{}) error { arr.mustNotBeBaseType() objVal := reflect.ValueOf(objs) if objVal.Elem().CanSet() { objVal.Elem().Set(arr.objs) return nil } return errors.New("element should be can set")}
使用方法:
func TestObjPointCollection_ToObjs(t *testing.T) { a1 := &Foo{A: "a1", B: 1} a2 := &Foo{A: "a2", B: 2} a3 := &Foo{A: "a3", B: 3} bArr := []*Foo{} objColl := NewObjPointCollection([]*Foo{a1, a2, a3}) err := objColl.ToObjs(&bArr) if err != nil { t.Fatal(err) } if len(bArr) != 3 { t.Fatal("toObjs error len") } if bArr[1].A != "a2" { t.Fatal("toObjs error copy") }}
总结
CanAddr 和 CanSet 刚接触的时候是会有一些懵逼,还是需要稍微理解下 reflect.Value 的 flag 就能完全理解了。
以上がCanSet、CanAddrとは何ですか?の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ホットAIツール

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

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

Undress AI Tool
脱衣画像を無料で

Clothoff.io
AI衣類リムーバー

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

人気の記事

ホットツール

メモ帳++7.3.1
使いやすく無料のコードエディター

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

ゼンドスタジオ 13.0.1
強力な PHP 統合開発環境

ドリームウィーバー CS6
ビジュアル Web 開発ツール

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

ホットトピック









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

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

Golang 単体テストでアサーションに Gomega を使用する方法 Golang 単体テストでは、Gomega は、開発者がテスト結果を簡単に検証できるように、豊富なアサーション メソッドを提供する人気のある強力なアサーション ライブラリです。 Gomegagoget-agithub.com/onsi/gomega をインストールする アサーションに Gomega を使用する アサーションに Gomega を使用する一般的な例をいくつか示します。 1. 等価アサーション import "github.com/onsi/gomega" funcTest_MyFunction(t*testing.T){

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

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

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

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

バックエンド学習パス:フロントエンドからバックエンドへの探査の旅は、フロントエンド開発から変わるバックエンド初心者として、すでにNodeJSの基盤を持っています...
