golang 反射(reflect)

golang 反射(reflect)

反射是現代程序必備的元素,用於在 運行時 獲取程序元素,如對象等的 元數據,實現動態識別類型及其結構,以及相關的語義信息。

反射在程序中應用非常多,例如:

在必要的場合,靈活應用反射,是中高級程序員能力的評價標準之一。靈活應用的根本是加深對 go 語言編譯與實現的理解,並閱讀典型應用案例。

濫用反射,也是低中級程序員最常見的問題,造成程序效率底下、不確定性錯誤增多。

一、Go 中的反射

go 是靜態語言,表示內存中任何一個數據對象(data object)的值及其類型必須是編譯期可確定的。因此,go 應用運行時不會像 java 等動態語言一樣,在運行期維護所有對象的元數據,以支持多態等需要。也不像 c 語言,不提供任何元數據支持。但註定 go 語言的反射是簡單和有限的。

大神文章,必讀!必讀!必讀!在短短的文章中,說明了 go 語言反射的要點!

請使用 $go tool tour 驗證該文中所有代碼!!!

這裏,僅提示其中要點:

package main

import "fmt"
import "reflect"

type T struct {
    A int
    B string
}

func (t *T) SetA(i int) {
    t.A = i
}

func main() {

    t := T{23, "skidoo"}
    s := reflect.ValueOf(&t).Elem()
    typeOfT := s.Type()
    for i := 0; i < s.NumField(); i++ {
        f := s.Field(i)
        fmt.Printf("%d: %s %s = %v\n", i,
        typeOfT.Field(i).Name, f.Type(), f.Interface())
    }
    typePT := reflect.TypeOf(&t)
    fmt.Printf("%d\n",typePT.NumMethod())
    for i := 0; i < typePT.NumMethod(); i++ {
        m := typePT.Method(i)
        fmt.Printf("%d: %s %v\n", m.Index,m.Name,m.Type)
    }
    s.Field(0).SetInt(77)
    s.Field(1).SetString("Sunset Strip")
    fmt.Println("t is now", t)

    //調用方法/函數
    m := typePT.Method(0)
    params := make([]reflect.Value,2) 
    params[0] = reflect.ValueOf(&t) 
    params[1] = reflect.ValueOf(5)
    m.Func.Call(params)
    fmt.Println("t is now", t)
}

參考:golang 反射中函數和方法的調用

二、golang 獲取包資源

程序中有許多資源,如配置文件、圖片、網頁等都是隨着包提供。對於 windows 程序或 java 程序都有 ResourceLoad 函數讀取運行程序(exe,dll,jar)中的資源。go 語言一般都源代碼提供,因此資源都是直接放置在包目錄下,而不打包。

go 包 爲你提供了按需管理程序資源的能力。其中,go/build 子包 是管理包以及應用環境最重要的包。

var Default Context = defaultContext()

Context 包含了程序構建工作區、版本等重要信息。

go tour 的源代碼,local.go 的 findRoot 函數提供查詢教學資源目錄的案例!https://github.com/golang/tour/blob/master/gotour/local.go

三、反射練習

設計一個簡單 ORMEngin 對象,使它完成以下任務:

數據庫表

CREATE TABLE `userinfo` (
    `uid` INT(10) NOT NULL AUTO_INCREMENT,
    `username` VARCHAR(64) NULL DEFAULT NULL,
    `departname` VARCHAR(64) NULL DEFAULT NULL,
    `created` DATE NULL DEFAULT NULL,
    PRIMARY KEY (`uid`)
);

1、orm 規則

我們在 field 對應的 Tag 中對 Column 的一些屬性進行定義,例如:

// UserInfo .
type UserInfo struct {
    UID        int   `orm:"id,auto-inc,type=INT(10)"` //語義標籤
    UserName   string
    DepartName string
    CreateAt   *time.Time `orm:"`
}

在 orm 標籤中,用 “,” 號作爲屬性的分割,每個屬性爲“key=value”。如果只有 key,表示它是 Bool 屬性,默認是 true。例如:id 表示這個字段是關鍵字。更多字段屬性可參考 Column 屬性定義 ,也可以用自己定義的規則和 key。

2、實現自動插入數據

用戶的樣例代碼:

user := UserInfo{...}
affected, err := engine.Insert(user)
// INSERT INTO user (name) values (?)

要求利用反射技術,根據輸入數據的類型自動生成插入 sql 語句,實現函數 Insert(o interface{})

3、實現查詢結果自動映射

用戶的樣例代碼:

pEveryOne := make([]*Userinfo, 0)
err := engine.Find(&pEveryOne)
// SELECT `col-name`,`col-name` ... FROM UserInfo

要求利用反射技術,根據輸入數據的類型自動生成查詢 sql 語句,並將結果集合根據數據類型自動映射到對象,並加入結果表。

提示

  1. 可以直接使用 database.sql 或使用 sqlt 。使用 sqlt 可以簡化程序開發,

  2. 任務 3 的結果映射,需要注意以下內容

package main

import (
    "fmt"
    _ "github.com/lib/pq"
    "database/sql"
)

func main() {

    db, _ := sql.Open(
        "postgres",
        "user=postgres db)

    rows, _ := db.Query("SELECT * FROM _user;")

    columns, _ := rows.Columns()
    count := len(columns)
    values := make([]interface{}, count)
    valuePtrs := make([]interface{}, count)

    for rows.Next() {

        for i, _ := range columns {
            valuePtrs[i] = &values[i]
        }

        rows.Scan(valuePtrs...)

        for i, col := range columns {
            var v interface{}
            val := values[i]
            b, ok := val.([]byte)

            if (ok) {
                v = string(b)
            } else {
                v = val
            }

            fmt.Println(col, v)
        }
    }
}

轉自:

blog.csdn.net/pmlpml/article/details/78850516

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/dF89ySHfv6rkhw2qT-jwqA