當前位置:
首頁 > 知識 > golang路上的小學生系列--使用reflect查找package路徑

golang路上的小學生系列--使用reflect查找package路徑

本文同時發布在個人博客chinazt.cc和gitbook

今日看到了一個有趣的golang項目--kolpa(https://github.com/malisit/kolpa)。 這個項目可以用來生成偽造的姓名,地址,時間,User-Agent等等信息,在需要大量隨機數據的測試環境中非常合適。

點擊fork之後,放在本地環境中build,run結果失敗。運行項目中提供的demo也失敗,按道理來說官方提供的demo應該都會成功,而且自己也沒有修改任何一行代碼,失敗是不科學的。

所以只能剖解代碼,查找失敗原因。

一查不知道,原來此項目需要依賴各個語言環境下的模板文件,而模板文件都放在項目的data目錄中。 在代碼中,通過硬編碼來確定模板文件位置:

// Reads the file "fName" and returns its content as a slice of strings.
func (g *Generator) fileToSlice(fName string) ([]string, error) {
var res string
path := os.Getenv("GOPATH") + "/src/github.com/malisit/kolpa/data/" + g.Locale + "/" + fName
file, err := os.Open(path)

if err != nil {
return nil, err
}
defer file.Close

scanner := bufio.NewScanner(file)
for scanner.Scan {
res = append(res, scanner.Text)
}

if err := scanner.Err; err != nil {
//log.Println("Inteded generation is not valid for selected language. Switching to en_US.")
return g.fileToSlice(fName)
}

return res, nil
}

因為我是通過fork,然後clone到本地的方式來運行demo,本地此時packge路徑已經不是上面的路徑了,所以導致運行失敗。 很難說這是一個bug,但的確影響到了程序運行。 所以說不良的代碼風格更為恰當吧。

既然找到了問題,那下一步就是如何解決問題。 應該如何在Runtime時實時獲取package位置呢?

說到Runtime,那麼一定就少不了Reflect package。

Package reflect implements run-time reflection, allowing a program to manipulate objects with arbitrary types. The typical use is to take a value with static type interface{} and extract its dynamic type information by calling TypeOf, which returns a Type.

從Reflect的介紹上看,Reflect package推薦的使用方式是通過TypeOf返回一個帶有interface{}所有動態類型信息的Type類型,然後通過Type類型來獲取各種程序需要的信息。

type Type interface {

// Align returns the alignment in bytes of a value of
// this type when allocated in memory.
Align int

// FieldAlign returns the alignment in bytes of a value of
// this type when used as a field in a struct.
FieldAlign int

// Method returns the i"th method in the type"s method set.
// It panics if i is not in the range [0, NumMethod).
//
// For a non-interface type T or *T, the returned Method"s Type and Func
// fields describe a function whose first argument is the receiver.
//
// For an interface type, the returned Method"s Type field gives the
// method signature, without a receiver, and the Func field is nil.
Method(int) Method

// MethodByName returns the method with that name in the type"s
// method set and a boolean indicating if the method was found.
//
// For a non-interface type T or *T, the returned Method"s Type and Func
// fields describe a function whose first argument is the receiver.
//
// For an interface type, the returned Method"s Type field gives the
// method signature, without a receiver, and the Func field is nil.
MethodByName(string) (Method, bool)

// NumMethod returns the number of exported methods in the type"s method set.
NumMethod int

// Name returns the type"s name within its package.
// It returns an empty string for unnamed types.
Name string

// PkgPath returns a named type"s package path, that is, the import path
// that uniquely identifies the package, such as "encoding/base64".
// If the type was predeclared (string, error) or unnamed (*T, struct{}, []int),
// the package path will be the empty string.
PkgPath string

// Size returns the number of bytes needed to store
// a value of the given type; it is analogous to unsafe.Sizeof.
Size uintptr

// String returns a string representation of the type.
// The string representation may use shortened package names
// (e.g., base64 instead of "encoding/base64") and is not
// guaranteed to be unique among types. To test for type identity,
// compare the Types directly.
String string

// Kind returns the specific kind of this type.
Kind Kind

// Implements reports whether the type implements the interface type u.
Implements(u Type) bool

// AssignableTo reports whether a value of the type is assignable to type u.
AssignableTo(u Type) bool

// ConvertibleTo reports whether a value of the type is convertible to type u.
ConvertibleTo(u Type) bool

// Comparable reports whether values of this type are comparable.
Comparable bool

// Bits returns the size of the type in bits.
// It panics if the type"s Kind is not one of the
// sized or unsized Int, Uint, Float, or Complex kinds.
Bits int

// ChanDir returns a channel type"s direction.
// It panics if the type"s Kind is not Chan.
ChanDir ChanDir

// IsVariadic reports whether a function type"s final input parameter
// is a "..." parameter. If so, t.In(t.NumIn - 1) returns the parameter"s
// implicit actual type T.
//
// For concreteness, if t represents func(x int, y ... float64), then
//
// t.NumIn == 2
// t.In(0) is the reflect.Type for "int"
// t.In(1) is the reflect.Type for "float64"
// t.IsVariadic == true
//
// IsVariadic panics if the type"s Kind is not Func.
IsVariadic bool

// Elem returns a type"s element type.
// It panics if the type"s Kind is not Array, Chan, Map, Ptr, or Slice.
Elem Type

// Field returns a struct type"s i"th field.
// It panics if the type"s Kind is not Struct.
// It panics if i is not in the range [0, NumField).
Field(i int) StructField

// FieldByIndex returns the nested field corresponding
// to the index sequence. It is equivalent to calling Field
// successively for each index i.
// It panics if the type"s Kind is not Struct.
FieldByIndex(index []int) StructField

// FieldByName returns the struct field with the given name
// and a boolean indicating if the field was found.
FieldByName(name string) (StructField, bool)

// FieldByNameFunc returns the struct field with a name
// that satisfies the match function and a boolean indicating if
// the field was found.
//
// FieldByNameFunc considers the fields in the struct itself
// and then the fields in any anonymous structs, in breadth first order,
// stopping at the shallowest nesting depth containing one or more
// fields satisfying the match function. If multiple fields at that depth
// satisfy the match function, they cancel each other
// and FieldByNameFunc returns no match.
// This behavior mirrors Go"s handling of name lookup in
// structs containing anonymous fields.
FieldByNameFunc(match func(string) bool) (StructField, bool)

// In returns the type of a function type"s i"th input parameter.
// It panics if the type"s Kind is not Func.
// It panics if i is not in the range [0, NumIn).
In(i int) Type

// Key returns a map type"s key type.
// It panics if the type"s Kind is not Map.
Key Type

// Len returns an array type"s length.
// It panics if the type"s Kind is not Array.
Len int

// NumField returns a struct type"s field count.
// It panics if the type"s Kind is not Struct.
NumField int

// NumIn returns a function type"s input parameter count.
// It panics if the type"s Kind is not Func.
NumIn int

// NumOut returns a function type"s output parameter count.
// It panics if the type"s Kind is not Func.
NumOut int

// Out returns the type of a function type"s i"th output parameter.
// It panics if the type"s Kind is not Func.
// It panics if i is not in the range [0, NumOut).
Out(i int) Type
// contains filtered or unexported methods
}

在Type介面定義中,和Package Path有關的有兩個函數:

  • PkgPath
  • String

PkgPath 返回指定類型的import package path,也就是說,如果代碼中有import encoding/base64這樣的語句, 那麼通過PkgPath就會返回encoding/base64,而不是base64package所在的實際路徑。 反言之,PkgPath返回的是import package path。

而如果繼續使用上例中的encoding/base64來說,String返回的是base64,而不是encoding/base64。 String返回的是實際使用的package name。

所以總結如下:

  • PkgPath 返回import package path
  • String 返回import package name

顯然,PkgPath更適合我們的需求。 簡單改造代碼如下:

1.增加 Pkg path

// Generator struct to access various generator functions
type Generator struct {
Locale string
Pkg string
}

2.獲取Pkg

// C is the creator function, initiates kolpa with or without locale
// setting. The default locale setting is "en_US".
// Returns a generator type that will be used to call generator methods.
func C(localeVar ...string) Generator {
newGenerator := Generator{}
if len(localeVar) > 0 {
newGenerator.Locale = localeVar[0]
} else {
newGenerator.Locale = "en_US"
}
// newGenerator.populateFunctions
newGenerator.Pkg = reflect.TypeOf(newGenerator).PkgPath
return newGenerator
}

3.替換硬編碼

// Reads the file "fName" and returns its content as a slice of strings.
func (g *Generator) fileToSlice(fName string) ([]string, error) {
var res string
path := os.Getenv("GOPATH") + "/src/" + g.Pkg + "/data/" + g.Locale + "/" + fName
file, err := os.Open(path)

if err != nil {
return nil, err
}
defer file.Close

scanner := bufio.NewScanner(file)
for scanner.Scan {
res = append(res, scanner.Text)
}

if err := scanner.Err; err != nil {
//log.Println("Inteded generation is not valid for selected language. Switching to en_US.")
return g.fileToSlice(fName)
}

return res, nil
}

4.運行demo

?> ~/S/g/g/s/t/kopla ./kopla
石潔玉
幸和平

完美生成兩個隨機姓名,話說"和平"也是一代人經常起的名字。

最後提一句,有的地方曾經說到盡量少使用Reflect package。 因此反射使用多了,影響效率。 我想這種想法應該是從Java VM那裡流傳出來的吧。 因為Java VM負責將位元組碼翻譯成機器碼,因此頻繁調用反射會加重VM切換上下文的代價,也就是把裝載期做的事情搬到了運行期,勢必降低運行效率。 golang的反射減少了翻譯環節,同時藉助於編譯器進行了代碼優化,雖然同樣在反射時需要進行額外的安全檢查和類型檢查,但不會降低太多效率。 而且上面,我們也看到只有在調用時也只發生了一次反射調用,影響幾乎可忽略不計。

喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 達人科技 的精彩文章:

14:40-15:00博客站點web伺服器雪崩似的CPU 100%
Elasticsearch學習隨筆(一)——原理理解與5.0核心插件部署過程
net 中web.config一個配置文件解決方法 (其他配置文件引入方式)
MVC通過遞歸+部分視圖實現評論
JDBC02 利用JDBC連接資料庫

TAG:達人科技 |

您可能感興趣

Spring Security 實現 antMatchers 配置路徑的動態獲取
通過路徑ControlLogix->1770KF2->OPC Client 傳送PLC2 type的message
如何使用Google Analytics 360中的高級分析探索訪客路徑?
Paint API之PathEffect(路徑效果)
SVG 路徑<path>
springboot丟失jdk路徑——jdk安裝與jdk多版本管理
經驗:解決Inno Setup 和一些應用程序在Windows 中不能訪問UNC路徑的問題
最小生成樹prime演算法、kruskal演算法 最短路徑演算法floyd、dijkstra
Creo/Preo軟體自學第二篇:部分配置文件在config中路徑的設置
使用xSignals定義高速信號路徑
從Margiela到Virgil,看看這幾年解構運動鞋的發展路徑啊
【乾貨】C盤空間不夠?如何更改Windows Update默認下載路徑
Python學習的一些路徑推薦
Python爬蟲 | 一條高效的學習路徑
tomcat配置虛擬路徑保存、訪問圖片
TalkingData發布2018年最新戰略布局,探索發展新路徑
Blast2go安裝之中間站:MySQL安裝及修改數據保存路徑
Photoshop巧用路徑工具設計曲扭圖標教程
推薦一條高效的Python爬蟲學習路徑!
模擬路徑預覽畫面,強大的《Vermeer》無人機AR應用誕生