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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...