模块
现在我们已经能写一些简单的代码了,今天我们来看下如何使用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:
		// ..
}