本文目錄一覽:
關於反射
在計算機科學領域,反射是指一類應用,它們能夠自描述和自控制。也就是說,這類應用通過採用某種機制來實現對自己行為的描述(self-representation)和監測(examination),並能根據自身行為的狀態和結果,調整或修改應用所描述行為的狀態和相關的語義。
每種語言的反射模型都不同,並且有些語言根本不支持反射。Golang語言實現了反射,反射機制就是在運行時動態的調用對象的方法和屬性,官方自帶的reflect包就是反射相關的,只要包含這個包就可以使用。
多插一句,Golang的gRPC也是通過反射實現的。
在講反射之前,先來看看Golang關於類型設計的一些原則
接下來要講的反射,就是建立在類型之上的,Golang的指定類型的變數的類型是靜態的(也就是指定int、string這些的變數,它的type是static type),在創建變數的時候就已經確定,反射主要與Golang的interface類型相關(它的type是concrete type),只有interface類型才有反射一說。
在Golang的實現中,每個interface變數都有一個對應pair,pair中記錄了實際變數的值和類型:
value是實際變數值,type是實際變數的類型。一個interface{}類型的變數包含了2個指針,一個指針指向值的類型【對應concrete type】,另外一個指針指向實際的值【對應value】。
例如,創建類型為*os.File的變數,然後將其賦給一個介面變數r:
介面變數r的pair中將記錄如下信息:(tty, *os.File),這個pair在介面變數的連續賦值過程中是不變的,將介面變數r賦給另一個介面變數w:
介面變數w的pair與r的pair相同,都是:(tty, *os.File),即使w是空介面類型,pair也是不變的。
interface及其pair的存在,是Golang中實現反射的前提,理解了pair,就更容易理解反射。反射就是用來檢測存儲在介面變數內部(值value;類型concrete type) pair對的一種機制。
既然反射就是用來檢測存儲在介面變數內部(值value;類型concrete type) pair對的一種機制。那麼在Golang的reflect反射包中有什麼樣的方式可以讓我們直接獲取到變數內部的信息呢? 它提供了兩種類型(或者說兩個方法)讓我們可以很容易的訪問介面變數內容,分別是reflect.ValueOf() 和 reflect.TypeOf(),看看官方的解釋
reflect.TypeOf()是獲取pair中的type,reflect.ValueOf()獲取pair中的value,示例如下:
當執行reflect.ValueOf(interface)之後,就得到了一個類型為」relfect.Value」變數,可以通過它本身的Interface()方法獲得介面變數的真實內容,然後可以通過類型判斷進行轉換,轉換為原有真實類型。不過,我們可能是已知原有類型,也有可能是未知原有類型,因此,下面分兩種情況進行說明。
已知類型後轉換為其對應的類型的做法如下,直接通過Interface方法然後強制轉換,如下:
示例如下:
很多情況下,我們可能並不知道其具體類型,那麼這個時候,該如何做呢?需要我們進行遍歷探測其Filed來得知,示例如下:
通過運行結果可以得知獲取未知類型的interface的具體變數及其類型的步驟為:
通過運行結果可以得知獲取未知類型的interface的所屬方法(函數)的步驟為:
reflect.Value是通過reflect.ValueOf(X)獲得的,只有當X是指針的時候,才可以通過reflec.Value修改實際變數X的值,即:要修改反射類型的對象就一定要保證其值是「addressable」的。
示例如下:
這算是一個高級用法了,前面我們只說到對類型、變數的幾種反射的用法,包括如何獲取其值、其類型、如果重新設置新值。但是在工程應用中,另外一個常用並且屬於高級的用法,就是通過reflect來進行方法【函數】的調用。比如我們要做框架工程的時候,需要可以隨意擴展方法,或者說用戶可以自定義方法,那麼我們通過什麼手段來擴展讓用戶能夠自定義呢?關鍵點在於用戶的自定義方法是未可知的,因此我們可以通過reflect來搞定
示例如下:
Golang的反射很慢,這個和它的API設計有關。在 java 裡面,我們一般使用反射都是這樣來弄的。
這個取得的反射對象類型是 java.lang.reflect.Field。它是可以復用的。只要傳入不同的obj,就可以取得這個obj上對應的 field。
但是Golang的反射不是這樣設計的:
這裡取出來的 field 對象是 reflect.StructField 類型,但是它沒有辦法用來取得對應對象上的值。如果要取值,得用另外一套對object,而不是type的反射
這裡取出來的 fieldValue 類型是 reflect.Value,它是一個具體的值,而不是一個可復用的反射對象了,每次反射都需要malloc這個reflect.Value結構體,並且還涉及到GC。
Golang reflect慢主要有兩個原因
上述詳細說明了Golang的反射reflect的各種功能和用法,都附帶有相應的示例,相信能夠在工程應用中進行相應實踐,總結一下就是:
嘗試用golang 1.18泛型實現orm
這幾天golang社區對泛型的討論非常多的,一片熱火朝天的景象。對我們廣大gopher來說總歸是好事。
泛型很有可能會顛覆我們之前的很多設計,帶著這種疑問和衝動,我準備嘗試用golang泛型實現幾個orm的常見功能。
本文並沒完全實現通用的orm,只是探討其實現的一種方式提供各位讀者做借鑒。
雖然golang有了泛型,但是目前在標準庫sql底層還沒有改造,目前還有很多地方需要用到reflect。
調用方式
這個部分跟傳統的orm使用上沒有太大區別,沒辦法不使用反射的情況下,泛型的方式可能變得有點繁瑣。
調用方式
和創建table類似,寫入數據好像比沒有之前的orm有優勢。
讀取數據是非常高頻的操作,所以我們稍作封裝。
調用方式
稍微比原先的orm方式有了多一點想像空間,比如 在[T any]做更明確的約束,比如要求實現Filter定製方法。
鑒於本人能力還認證有限,目前還沒有發現泛型對orm劇烈的改進和突破的可能。未來如果go對底層sql做出改動,或者實現諸如Rust那種Enum方式,可能會帶來更多的驚喜。
golang反射框架Fx
Fx是一個golang版本的依賴注入框架,它使得golang通過可重用、可組合的模塊化來構建golang應用程序變得非常容易,可直接在項目中添加以下內容即可體驗Fx效果。
Fx是通過使用依賴注入的方式替換了全局通過手動方式來連接不同函數調用的複雜度,也不同於其他的依賴注入方式,Fx能夠像普通golang函數去使用,而不需要通過使用struct標籤或內嵌特定類型。這樣使得Fx能夠在很多go的包中很好的使用。
接下來會提供一些Fx的簡單demo,並說明其中的一些定義。
1、一般步驟
大致的使用步驟就如下。下面會給出一些完整的demo
2、簡單demo
將io.reader與具體實現類關聯起來
輸出:
3、使用struct參數
前面的使用方式一旦需要進行注入的類型過多,可以通過struct參數方式來解決
輸出
如果通過Provide提供構造函數是生成相同類型會有什麼問題?換句話也就是相同類型擁有多個值呢?
下面兩種方式就是來解決這樣的問題。
4、使用struct參數+Name標籤
在Fx未使用Name或Group標籤時不允許存在多個相同類型的構造函數,一旦存在會觸發panic。
輸出
上面通過Name標籤即可完成在Fx容器注入相同類型
5、使用struct參數+Group標籤
使用group標籤同樣也能完成上面的功能
輸出
基本上Fx簡單應用在上面的例子也做了簡單講解
1、Annotated(位於annotated.go文件) 主要用於採用annotated的方式,提供Provide注入類型
源碼中Name和Group兩個欄位與前面提到的Name標籤和Group標籤是一樣的,只能選其一使用
2、App(位於app.go文件) 提供注入對象具體的容器、LiftCycle、容器的啟動及停止、類型變數及實現類注入和兩者映射等操作
至於Provide和Populate的源碼相對比較簡單易懂在這裡不在描述
具體源碼
3、Extract(位於extract.go文件)
主要用於在application啟動初始化過程通過依賴注入的方式將容器中的變數值來填充給定的struct,其中target必須是指向struct的指針,並且只能填充可導出的欄位(golang只能通過反射修改可導出並且可定址的欄位),Extract將被Populate代替。 具體源碼
4、其他
諸如Populate是用來替換Extract的,而LiftCycle和inout.go涉及內容比較多後續會單獨提供專屬文件說明。
在Fx中提供的構造函數都是惰性調用,可以通過invocations在application啟動來完成一些必要的初始化工作:fx.Invoke(function); 通過也可以按需自定義實現LiftCycle的Hook對應的OnStart和OnStop用來完成手動啟動容器和關閉,來滿足一些自己實際的業務需求。
Fx框架源碼解析
主要包括app.go、lifecycle.go、annotated.go、populate.go、inout.go、shutdown.go、extract.go(可以忽略,了解populate.go)以及輔助的internal中的fxlog、fxreflect、lifecycle
如何利用golang 反射值來定義一個變數
之前寫java的時候就已經知道了有反射這個概念,看過一遍文章專門介紹了一下java的反射機制,不過現在忘記了。
今天寫一個通用函數的時候要對傳入的參數經行類型判斷,還要定義與其像同類型的變數經行取址運算,baidu, google,golang reflect包也看了好久,也沒看到將返回值來定義個變數的辦法。最後在一篇博客中得到靈感—–點擊可以看看。
這篇文章中沒有告訴怎麼將反射值去定義一個變數,但看完之後思考一下,既然要返回一個類型,但是類型又是不確定的,若存在這樣一個返回變數的函數,則其返回類型必是Interface,在官網文檔里找返回類型是Interface的函數,就是它了,它是Value類型的一個函數,所以就不能使用reflect.TypeOf(i interface{})來操作了,就只能用reflect.ValueOf().於是慢慢結合上訴博客加文檔,get~
package main
import (
“reflect”
“fmt”
)
func main() {
v2 := “tangs”
fmt.Println(“v2’s value is : “, v2, “, type is : “, reflect.TypeOf(v2))
ty := reflect.ValueOf(v2).Elem()
s := ty.Interface()
s = “tangs”
fmt.Println(“s’s value is : “, s, “, type is : ” ,reflect.TypeOf(s))
}
golang怎麼new一個對象
因為golang的反射沒有JAVA的那麼強大,但是在大多數情況下已經夠用了。 golang不支持解析string然後執行。 golang的反射機制只能存在於已經存在的對象上面。
原創文章,作者:EOGI,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/135276.html