go-反射

来源:https://www.cnblogs.com/ygjzs/archive/2019/11/21/11909113.html
-Advertisement-
Play Games

反射 反射的基本介紹 17.3.1 基本介紹 1) 反射可以在運行時 動態獲取變數的各種信息, 比如變數的類型(type),類別(kind) 2) 如果是結構體變數,還可以獲取到結構體本身的信息(包括結構體的 欄位、 方法) 3) 通過反射,可以修改變數的值,可以調用關聯的方法。 4) 使用反射,需 ...


反射

反射的基本介紹

17.3.1 基本介紹
1) 反射可以在運行時 動態獲取變數的各種信息, 比如變數的類型(type),類別(kind)
2) 如果是結構體變數,還可以獲取到結構體本身的信息(包括結構體的 欄位、 方法)
3) 通過反射,可以修改變數的值,可以調用關聯的方法。
4) 使用反射,需要 import (“reflect”)

反射重要的函數和概念

1) reflect.TypeOf(變數名),獲取變數的類型,返回reflect.Type

2) reflect.Value(變數名),獲取變數的值,返回reflect.Value(是一個結構體類型)
3) 變數、interface{} 和 reflect.Value 是可以相互轉換的,這點在實際開發中,會經常使用到

反射的快速入門

快速入門說明

請編寫一個案例,演示對(基本數據類型、interface{}、reflect.Value)進行反射的基本操作

代碼演示,見下麵的表格:
請編寫一個案例,演示對(結構體類型、interface{}、reflect.Value)進行反射的基本操作

package main
import (
    "reflect"
    "fmt"
)


//專門演示反射
func reflectTest01(b interface{}) {

    //通過反射獲取的傳入的變數的 type , kind, 值
    //1. 先獲取到 reflect.Type
    rTyp := reflect.TypeOf(b)
    fmt.Println("rType=", rTyp)

    //2. 獲取到 reflect.Value
    rVal := reflect.ValueOf(b)
    
    n2 := 2 + rVal.Int()
    //n3 := rVal.Float()
    fmt.Println("n2=", n2)
    //fmt.Println("n3=", n3)
    
    fmt.Printf("rVal=%v rVal type=%T\n", rVal, rVal)
    fmt.Printf("rVal=%v rTyp type=%T\n", rVal, rTyp)

    //下麵我們將 rVal 轉成 interface{}
    iV := rVal.Interface()
    //將 interface{} 通過斷言轉成需要的類型
    num2 := iV.(int)
    fmt.Println("num2=", num2)


}

//專門演示反射[對結構體的反射]
func reflectTest02(b interface{}) {

    //通過反射獲取的傳入的變數的 type , kind, 值
    //1. 先獲取到 reflect.Type
    rTyp := reflect.TypeOf(b)
    fmt.Println("rType=", rTyp)

    //2. 獲取到 reflect.Value
    rVal := reflect.ValueOf(b)

    //3. 獲取 變數對應的Kind
    //(1) rVal.Kind() ==> 
    kind1 := rVal.Kind()
    //(2) rTyp.Kind() ==>
    kind2 := rTyp.Kind()
    fmt.Printf("kind =%v kind=%v\n", kind1, kind2)
    


    //下麵我們將 rVal 轉成 interface{}
    iV := rVal.Interface()
    fmt.Printf("iv=%v iv type=%T \n", iV, iV)
    //將 interface{} 通過斷言轉成需要的類型
    //這裡,我們就簡單使用了一帶檢測的類型斷言.
    //同學們可以使用 swtich 的斷言形式來做的更加的靈活
    stu, ok := iV.(Student)
    if ok {
        fmt.Printf("stu.Name=%v\n", stu.Name)
    }

}

type Student struct {
    Name string
    Age int
}

type Monster struct {
    Name string
    Age int
}

func main() {

    //請編寫一個案例,
    //演示對(基本數據類型、interface{}、reflect.Value)進行反射的基本操作

    //1. 先定義一個int
    var num int = 100
    reflectTest01(num)

    //2. 定義一個Student的實例
    stu := Student{
        Name : "tom",
        Age : 20,
    }
    reflectTest02(stu)


}

反射的註意事項和細節

1) reflect.Value.Kind,獲取變數的類別,返回的是一個常量
2) Type 和 Kind 的區別:

Type 是類型, Kind 是類別, Type 和 Kind 可能是相同的,也 可能是不同的.
比如: var num int = 10 num 的 Type 是 int , Kind 也是 int
比如: var stu Student stu 的 Type 是 pkg1.Student , Kind 是 struct

3) 通過反射可以讓變數在interface{}和reflect.Value之間互相轉換。
4) 通過反射獲取的變數的值,要求數據類型配,比如x是int,那麼就應該用reflect.Value.int(),而不能用其他,否則會報panic的錯誤。
5) 通過反射的來修改變數, 註意當使用 SetXxx 方法來設置需要通過對應的指針類型來完成, 這樣才能改變傳入的變數的值, 同時需要使用到 reflect.Value.Elem()方法


package main
import (
    "reflect"
    "fmt"
)

//通過反射,修改,
// num int 的值
// 修改 student的值

func reflect01(b interface{}) {
    //2. 獲取到 reflect.Value
    rVal := reflect.ValueOf(b)
    // 看看 rVal的Kind是 
    fmt.Printf("rVal kind=%v\n", rVal.Kind())
    //3. rVal
    //Elem返回v持有的介面保管的值的Value封裝,或者v持有的指針指向的值的Value封裝
    rVal.Elem().SetInt(20)
}

func main() {

    var num int = 10
    reflect01(&num)
    fmt.Println("num=", num) // 20


    //你可以這樣理解rVal.Elem()
    // num := 9
    // ptr *int = &num
    // num2 := *ptr  //=== 類似 rVal.Elem()
}
package main
import (
    "fmt"
    "reflect"
)
func main() {
    var str string = "tom"   //ok
    fs := reflect.ValueOf(&str) //ok fs -> string
    fs.Elem().SetString("jack") //ok
    fmt.Printf("%v\n", str) // jack
}

反射最佳實踐

1) 使用 反射來遍歷結構體的欄位, 調用結構體的方法,並 獲取結構體標簽的值
2) 使用反射的方式來獲取結構體的 tag 標簽, 遍歷欄位的值,修改欄位值,調用結構體方法(要求:通過傳遞地址的方式完成, 在前面案例上修改即可)

3) 定義了兩個函數 test1 和 test2,定義一個適配器函數用作統一處理介面(略,用反射實現即可)
4) 使用反射操作任意結構體類型:
5) 使用反射創建並操作結構體

package main
import (
    "fmt"
    "reflect"
)
//定義了一個Monster結構體
type Monster struct {
    Name  string `json:"name"`
    Age   int `json:"monster_age"`
    Score float32 `json:"成績"`
    Sex   string
    
}

//方法,返回兩個數的和
func (s Monster) GetSum(n1, n2 int) int {
    return n1 + n2
}
//方法, 接收四個值,給s賦值
func (s Monster) Set(name string, age int, score float32, sex string) {
    s.Name = name
    s.Age = age
    s.Score = score
    s.Sex = sex
}

//方法,顯示s的值
func (s Monster) Print() {
    fmt.Println("---start~----")
    fmt.Println(s)
    fmt.Println("---end~----")
}
func TestStruct(a interface{}) {
    //獲取reflect.Type 類型
    typ := reflect.TypeOf(a)
    //獲取reflect.Value 類型
    val := reflect.ValueOf(a)
    //獲取到a對應的類別
    kd := val.Kind()
    //如果傳入的不是struct,就退出
    if kd !=  reflect.Struct {
        fmt.Println("expect struct")
        return
    }

    //獲取到該結構體有幾個欄位
    num := val.NumField()

    fmt.Printf("struct has %d fields\n", num) //4
    //變數結構體的所有欄位
    for i := 0; i < num; i++ {
        fmt.Printf("Field %d: 值為=%v\n", i, val.Field(i))
        //獲取到struct標簽, 註意需要通過reflect.Type來獲取tag標簽的值
        tagVal := typ.Field(i).Tag.Get("json")
        //如果該欄位於tag標簽就顯示,否則就不顯示
        if tagVal != "" {
            fmt.Printf("Field %d: tag為=%v\n", i, tagVal)
        }
    }
    
    //獲取到該結構體有多少個方法
    numOfMethod := val.NumMethod()
    fmt.Printf("struct has %d methods\n", numOfMethod)
    
    //var params []reflect.Value
    //方法的排序預設是按照 函數名的排序(ASCII碼)
    val.Method(1).Call(nil) //獲取到第二個方法。調用它

    
    //調用結構體的第1個方法Method(0)
    var params []reflect.Value  //聲明瞭 []reflect.Value
    params = append(params, reflect.ValueOf(10))
    params = append(params, reflect.ValueOf(40))
    res := val.Method(0).Call(params) //傳入的參數是 []reflect.Value, 返回[]reflect.Value
    fmt.Println("res=", res[0].Int()) //返回結果, 返回的結果是 []reflect.Value*/

}
func main() {
    //創建了一個Monster實例
    var a Monster = Monster{
        Name:  "黃鼠狼精",
        Age:   400,
        Score: 30.8,
    }
    //將Monster實例傳遞給TestStruct函數
    TestStruct(a)   
}

const介紹

package main
import (
    
    "fmt"
)
func main() {

    var num int
    num = 9 //ok
    //常量聲明的時候,必須賦值。
    const tax int = 0 
    //常量是不能修改
    //tax = 10
    fmt.Println(num, tax)
    //常量只能修飾bool、數值類型(int, float系列)、string 類型
    
    //fmt.Println(b)

    const (
        a = iota
        b 
        c
        d
    )


    fmt.Println(a, b, c, d)//0 1 2 3
}



您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • '''''' ''' 一、函數 1、函數定義 對功能或者動作的封裝 在類中定義,就是方法 在類之外定義,就是函數 2、函數寫法 1、定義或者申明函數 def 函數名(形參列表): 函數體(return) 2、調用函數 函數名(實參) 3、函數名 1、定義 是一個變數的名字(函數名也屬於變數) 2、用... ...
  • 例15 除法算式 問題描述 輸入正整數n(2≤n≤68),按從小到大輸出所有形如abcde/fghi=n的表達式。其中a~i為1~9的一個排列。 輸入格式 每行為一個正整數n (n <= 1500),輸入n=0結束。 輸出格式 輸出滿足條件的所有形如abcde/fghi=n的表達式,每個表達式占一行 ...
  • 異常處理 異常分類與體繫結構: Error 相關類型的異常是程式無法處理(大多都是修改代碼無法解決的)的異常,這類異常通常需要我們調整JVM的運行環境 Exception 相關類型的異常是程式可以處理的異常,其包含兩大子類型 編譯異常(CheckedException) 通常是語法錯誤,或是方法明確 ...
  • 概述: 在Java中存在一些基本數據類型,這些基本數據類型變數,不能像其他對象一樣調用方法,屬性.... 一些情況下帶來一些問題,包裝類就是為瞭解決這個問題而出現 包裝類可以使得這些基礎數據類型,擁有對象的能力 包裝類與基礎類型的對應關係 特點: 包裝類都是final修飾無法繼承 數字類型的父類都是 ...
  • 進程管理控制 這裡實現的是一個自定義timer用於統計子進程運行的時間。使用方式主要是 例如要統計 的運行時間可以直接輸入 ,其後的 是指所要運行的程式的參數。如: 。如果要指定程式運行多少時間,如5秒鐘,可以輸入 。需要註意的是,該程式對輸入沒有做異常檢測,所以要確保程式輸入正確。 Linux 程 ...
  • 線程狀態概述: 當線程被創建並啟動以後,它既不是一啟動就進入了執行狀態,也不是一直處於執行狀態。線上程的生命周期中, 有幾種狀態呢?在API中 java.lang.Thread.State 這個枚舉中給出了六種線程狀態 Timed Waiting(計時等待) Timed Waiting在API中的描 ...
  • 連接上imap服務後,什麼都不操作,我測試大約5分鐘會被服務端斷掉,測試代碼如下 為了保持住這條連接,每隔10秒列取一下郵件夾列表,這樣就可以一直保持住連接了。開三個視窗,一個視窗不停的netstat查看tcp連接情況,一個視窗運行代碼,一個視窗打開tcpdump監聽埠查看數據請求 while t ...
  • 本文介紹,PHP運行在FastCGI模式時,FPM提供的方法:fastcgi_finish_request。在說這個方法之前,我們先瞭解PHP有哪些常用的運行模式? PHP運行模式CGI 通用網關介面 / Common Gateway InterfaceCGI已經是比較老的模式了,這幾年都很少用了。 ...
一周排行
    -Advertisement-
    Play Games
  • Dapr Outbox 是1.12中的功能。 本文只介紹Dapr Outbox 執行流程,Dapr Outbox基本用法請閱讀官方文檔 。本文中appID=order-processor,topic=orders 本文前提知識:熟悉Dapr狀態管理、Dapr發佈訂閱和Outbox 模式。 Outbo ...
  • 引言 在前幾章我們深度講解了單元測試和集成測試的基礎知識,這一章我們來講解一下代碼覆蓋率,代碼覆蓋率是單元測試運行的度量值,覆蓋率通常以百分比表示,用於衡量代碼被測試覆蓋的程度,幫助開發人員評估測試用例的質量和代碼的健壯性。常見的覆蓋率包括語句覆蓋率(Line Coverage)、分支覆蓋率(Bra ...
  • 前言 本文介紹瞭如何使用S7.NET庫實現對西門子PLC DB塊數據的讀寫,記錄了使用電腦模擬,模擬PLC,自至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1.Windows環境下鏈路層網路訪問的行業標準工具(WinPcap_4_1_3.exe)下載鏈接:http ...
  • 從依賴倒置原則(Dependency Inversion Principle, DIP)到控制反轉(Inversion of Control, IoC)再到依賴註入(Dependency Injection, DI)的演進過程,我們可以理解為一種逐步抽象和解耦的設計思想。這種思想在C#等面向對象的編 ...
  • 關於Python中的私有屬性和私有方法 Python對於類的成員沒有嚴格的訪問控制限制,這與其他面相對對象語言有區別。關於私有屬性和私有方法,有如下要點: 1、通常我們約定,兩個下劃線開頭的屬性是私有的(private)。其他為公共的(public); 2、類內部可以訪問私有屬性(方法); 3、類外 ...
  • C++ 訪問說明符 訪問說明符是 C++ 中控制類成員(屬性和方法)可訪問性的關鍵字。它們用於封裝類數據並保護其免受意外修改或濫用。 三種訪問說明符: public:允許從類外部的任何地方訪問成員。 private:僅允許在類內部訪問成員。 protected:允許在類內部及其派生類中訪問成員。 示 ...
  • 寫這個隨筆說一下C++的static_cast和dynamic_cast用在子類與父類的指針轉換時的一些事宜。首先,【static_cast,dynamic_cast】【父類指針,子類指針】,兩兩一組,共有4種組合:用 static_cast 父類轉子類、用 static_cast 子類轉父類、使用 ...
  • /******************************************************************************************************** * * * 設計雙向鏈表的介面 * * * * Copyright (c) 2023-2 ...
  • 相信接觸過spring做開發的小伙伴們一定使用過@ComponentScan註解 @ComponentScan("com.wangm.lifecycle") public class AppConfig { } @ComponentScan指定basePackage,將包下的類按照一定規則註冊成Be ...
  • 操作系統 :CentOS 7.6_x64 opensips版本: 2.4.9 python版本:2.7.5 python作為腳本語言,使用起來很方便,查了下opensips的文檔,支持使用python腳本寫邏輯代碼。今天整理下CentOS7環境下opensips2.4.9的python模塊筆記及使用 ...