主要介绍在 Golang 中相对进阶的用法,如interface、reflection、Tag。善用这些技巧可以使得程序码更加简洁。ex, 透过 interface 的技巧使得 func 的参数更加有弹性;使用 reflection 进一步资料属於的型态、甚至达到无须知道 type 也能够修改资料; Tag 让你 mapping 资料更加方便。
Interface{} 型别转换
在Golang 中Interface
资料结构是相当重要的,由於 Golang 属於强型别语言,因此在func 中的 parameter 与 return value 时常会因为结构受限造成许多的不方便,然而interface
就是来解决问题。(它本身可以是Golang 语言中任一 type
进而解决该问题。) @playground
package main
import (
"fmt"
"strconv"
)
type str string
func (s str) String() string {
return string(s)
}
type Stringer interface {
String() string
}
func ToString(any interface{}) string {
if v, ok := any.(Stringer); ok {
return v.String()
}
switch v := any.(type) {
case int:
return strconv.Itoa(v)
case float64:
return strconv.FormatFloat(v, 'g', -1, 64)
}
return "???"
}
func main() {
var ex int = 1
fmt.Println(ToString(ex))
var ex2 float64 = 0.1
fmt.Println(ToString(ex2))
var ex3 Stringer = str("1")
fmt.Println(ToString(ex3))
}
如上述程序码,实作ToString 方法时,需要传入各种型态(type)的参数,此时Interface 的弹性就派上用场了,藉由还原的语法进行实作,绕过强型别参数型态固定的问题。
Interface{} 多形
此外在Golang中若要做到 abstract method 的话则也需要 interface
,它提供抽象方法的功能,并且可以在compile time 就能排除抽象方法实作上部份错误,ex. 少定义方法等...。换句话说,在Golang中实作多型
须仰赖interface
。@playground
package main
import (
"fmt"
)
type Car interface {
AddOil(gallon int)
Run()
}
type VovoCar struct {
}
func (v *VovoCar) AddOil(gallon int) {
fmt.Printf("vovo car add %d gallon\n", gallon)
}
func (v *VovoCar) Run() {
fmt.Printf("vovo car add run\n")
}
type ToyotaCar struct {
}
func (t *ToyotaCar) AddOil(gallon int) {
fmt.Printf("Toyota car add %d gallon\n", gallon)
}
func (t *ToyotaCar) Run() {
fmt.Printf("Toyota car add run\n")
}
func main() {
var c Car = &VovoCar{}
var c2 Car = &ToyotaCar{}
c.AddOil(10)
c.Run()
c2.AddOil(100)
c2.Run()
}
Reflection
是一种用於描述程序语言的工具。由於在Golang 中任何型别都可以是一种Interface
,因此时常需要与Reflection
进行搭配,故笔者认为是一种Interface
的配套工具。主要有三种用法:
package main
import (
"fmt"
"reflect"
)
type Example struct {}
func main() {
fmt.Println(reflect.TypeOf(&Example{})) // *main.Eaxmple
}
package main
import (
"fmt"
"reflect"
)
type Example struct {
name string
}
func main() {
val := reflect.ValueOf(&Example{ name: "Example name"}).Interface().(*Example)
fmt.Println(val) // &{Example name}
}
Ptr
。reflect.ValueOf.Elem.Field
找出struct filed 的位置进而修改资料。package main
import (
"fmt"
"reflect"
)
type Example struct {
Id string
Name string
}
func ChangeValue(dest interface{}) {
valDest := reflect.ValueOf(dest)
for i := 0; i < valDest.Elem().NumField(); i++ {
if i == 0 {
valDest.Elem().Field(i).Set(reflect.ValueOf("change_id"))
} else {
valDest.Elem().Field(i).Set(reflect.ValueOf("change_name"))
}
}
}
func main() {
destVal := &Example{Id: "test_id", Name: "test_name"}
fmt.Println(destVal) // &{test_id test_name}
ChangeValue(destVal)
fmt.Println(destVal) // &{change_id change_name}
}
在Golang Struct 资料结构中,可自定义 Tag,有点类似於其他语言的Annotation,例如在判读 Json 的Key 值时,须利用`json:"
name"`
的方式填入。然而在学习Golang 的初期时常会勿以为 Tag 是不可定义的,因为并不清楚如何取用。然而Reflection 工具此时就得到了一大作用,由於是描述程序语言的工具,因此可透过该工具将Tag 取出。@playground
package main
import (
"fmt"
"reflect"
)
type Example struct {
Id string `json:"id"`
}
func main() {
jsonExample := &Example{Id: "test"}
field := reflect.TypeOf(*jsonExample).Field(0)
fmt.Printf("name: %v ,tag:'%v'\n", field.Name, field.Tag)
// name: Id ,tag:'json:"id"'
}
在Golang 语言中除了正规的正常的写法之外,也提供低阶的程序设计模式,如可直接调用Unix 系统的C程序的cgo。但这种方式是不被建议使用的,因为直接调用很容易出现非预期的错误,除非特殊需要。若要Golang 中使用这一类低阶的程序设计,则需宣告import "unsafe"
字样,语意上表示从外部汇入unsafe 套件,但实际上是由编译器直接调用隐藏功能。ex: net, syscall, os, runtime 等大多程序设计不太需要用到的。
*ArbitraryType
而 ArbitraryType 的定义为type ArbitraryType int
,因此实际上也是一种 Integer 型别。指的是在编译器端,对於程序码编译後,针对指标 (Pointer) 资料结构进行优化,并计算出需要多少的Heap 储存。往往在Golang 中,时常使用到指标类型的结构,然而只要有该函式之外的呼叫,编译器则会预先预先多配发空间,进而触发GC(Grabage Collection)机制。
例如以下图式为例: 单纯透过指标的方式印出整数阵列,正规写法如Listing 1 直接使用外部func 印出,优化做法透过 unsafe 的方式呼叫外部func。
在Golang中使用unsafe 直接调用Compile 的Pointer编译器则不会预先产出Heap,也因此能避免该问题,但坏处是可能会遇到预期之外的错误(警语)。如上图程序码,都只是印出数字,但透过 unsafe pointer 调用的,使用的记忆体大小是预设的50倍,Heap 的大小是 3倍(下图所示)。
[1] Wang, Cong, et al. "Escape from escape analysis of Golang." Proceedings of the ACM/IEEE 42nd International Conference on Software Engineering: Software Engineering in Practice. 2020.
[2] https://go.dev/blog/laws-of-reflection
[3] https://research.swtch.com/interfaces
<<: 【从零开始的 C 语言笔记】第十八篇-For Loop
>>: Methods to Login to Your ATT Account
峰禾影视 峰禾影视电影电视剧频道拥有海量热门经典和2021最新的电影电视剧等,优质影视大片高清免费在...
哈罗~ 今天要再来介绍另一个隐写工具... 我真的常常都不知道开头要打什麽ಥ︿ಥ 那就..直接开始吧...
include vs extend vs prepend include、extend、prepen...
相信在座的看倌都知道 marge、padding 是做什麽用的,还不知道的同学可能要自行补一下 C...
Array 是一个有序的集合,可以存储相同类型的元素。 初始化: // 1. init时就有值 NS...