模块
现在我们已经能写一些简单的代码了,今天我们来看下如何使用Go module来进行多个模块的代码编写。
mkdir hello
cd hello
go mod init example.com/hello
我们通过pkg.go.dev搜索我们需要的包,并将该包使用import
引入到我们的hello.go
文件中。
package main
import "fmt"
import "rsc.io/quote"
func main() {
fmt.Println(quote.Go())
}
然后我们运行go mod tidy
来进行自动的添加或删除需要的包。
go mod tidy
go run .
运行后我们发现本地多了一个go.mod的文件,打开该文件后发现引用中带了一个版本的选项,用模块版本编号的每个部分来表示版本的稳定性和向后兼容性,同时也反映了较上个版本以来模块更改的性质。
require rsc.io/quote v1.5.2
现在调用外部模块已经成功了,我们在本地创建个模块试着调用下。
mkdir greetings
cd greetings
go mod init example.com/greetings
# 创建一个greetings.go文件
cat > greetings.go << EOF
package greetings
import "fmt"
func Hello(name string) string {
message := fmt.Sprintf("Hi, %v. Welcome!", name)
return message
}
EOF
模块创建好后我们使用tree .
查看下当前的目录结构。
.
├── greetings
│ ├── go.mod
│ └── greetings.go
└── hello
├── go.mod
└── hello.go
修改hello/hello.go
的代码,调用我们自己的本地模块greetings
。
package main
import (
"fmt"
"example.com/greetings"
)
func main() {
out := greetings.Hello("小黑")
fmt.Println(out)
}
通过上面的操作我们知道go mod tidy
会自动查找并下载依赖,但由于我们的模块是在本地任意创建的未进行发布,所以需要使用go mod edit
调整下我们模块的地址。
go mod edit -replace example.com/greetings=../greetings
执行后我们会发现go.mod
多了一个行
replace example.com/greetings => ../greetings
配置完我们就可以直接go mod tidy
后运行代码了,当我们模块写的比较完善后,就可以进行模块发布了
go mod tidy
go run .
反射
反射是程序自身可以访问和检测自身状态的一种能力,在我们写Go代码中可能很少用到,但有时合理的利用反射能让我们的代码更简洁和灵活。Go中使用reflect实现了反射的能力,核心是reflect.TypeOf和reflect.ValueOf两个方法。
func TypeOf(i any) Type : 获取动态类型i的反射信息,然后我们看下返回的reflect.Type类型。
// 调用TypeOf
var x float64 = 3.4
fmt.Println("type:", reflect.TypeOf(x))
// 查看Type定义
type Type interface {
Align() int
FieldAlign() int
Method(int) Method
MethodByName(string) (Method, bool)
NumMethod() int
Name() string
PkgPath() string
Size() uintptr
String() string
Kind() Kind
Implements(u Type) bool
// ...
}
我们跟踪进去查看下reflect.TypeOf的具体实现是将参数强制转换为emptyInterface,而emptyInterface中实现的reflect/type.go:rtype和系统runtime/type.go:_type中的完全一致,是直接同步的,所以可以直接强制转换。
func TypeOf(i any) Type {
eface := *(*emptyInterface)(unsafe.Pointer(&i))
return toType(eface.typ)
}
func toType(t *rtype) Type {
if t == nil {
return nil
}
return t
}
func ValueOf(i any) Value : 获取类型i的具体值信息,然后我们看下返回的reflect.Value类型,reflect.Value没有提供任何公开字段,但提供了获取和写入的一些方法。
// 调用ValueOf
var x float64 = 3.4
v := reflect.ValueOf(x)
fmt.Println("type:", v.Type())
fmt.Println("value:", v.Float())
// 查看Value定义
type Value struct {
// 包含过滤或未导出的字段
}
func (v Value) Addr() Value
func (v Value) Bool() bool
// 赋值
func (v Value) Set(x Value)
// 类型
func (v Value) Type() Type
// 返回接口值或指针
func (v Value) Elem() Value
// ...
文章《The Laws of Reflection》中总结了反射的三大定律。
- 反射可以从接口值中获得反射对象。如:reflect.TypeOf() / reflect.ValueOf()
- 反射可以从反射对象中获得接口值。如:y := v.Interface().(float64)
- 要修改反射对象,其值必须可设置。如:值类型的变量不可设置
我们常见的有以下几种使用方式:
// 无参调用
reflect.ValueOf(t).MethodByName(name).Call(nil)
// 有参调用
value := []reflect.Value{a, b}
reflect.ValueOf(t).MethodByName(name).Call(value)
// 接收返回值
ret := reflect.ValueOf(t).MethodByName(name).Call(nil)
// 类型判断
t := reflect.TypeOf(a)
switch t.Kind(){
case reflect.Int:
// ..
case reflect.String:
// ..
}