📌Golang📌基础📌R-反射.txt
Go语言提供了一种机制在运行时更新变量和检查它们的值、调用它们的方法,但是在编译时并不知道这些变量的具体类型,这称为反射机制。
反射带来的灵活性是一把双刃剑,作为一种元编程方式可以减少重复代码,但是过量的使用会使程序逻辑变得难以理解并且运行缓慢。
标准库"reflect"包实现了运行时的反射能力,能够让程序操作不同类型的对象。
TypeOf函数返回Type类型信息,ValueOf函数返回Value数据的运行时表示。通过Elem()获得指针指向的类型。

Type接口表示反射类型,Value结构体表示反射对象,Kind常量表示反射种类。
不是所有类型都可以调用Type/Value的全部方法,需先使用Kind方法判断类型后再调用相应的特定方法,调用非该类型支持的方法会panic。

Laws of Reflection:
    Reflection goes from interface value to reflection object.
    Reflection goes from reflection object to interface value.
    To modify a reflection object, the value must be settable.

========== ========== 简单使用 ========== ==========

打印底层值的类型
{
	for _, item := range []any{'a', float32(3.14), "hi", []byte{127, 0}} {
		switch v := reflect.ValueOf(item); v.Kind() {
		case reflect.String:
			fmt.Println("string:", v.String())
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
			fmt.Println("int:", v.Int())
		case reflect.Float32, reflect.Float64:
			fmt.Println("float:", v.Float())
		default:
			fmt.Println("other kind:", v.Kind())
		}
	}
}
一般场景使用type-switch断言类型即可,非必要不使用反射。

打印变量类型名称,可用于尝试获得错误类型
{
	a := "a"
	b := &a
	c := &b
	fmt.Println(
		reflect.TypeOf(a).String(),     //string
		reflect.TypeOf(b).String(),     //*string
		reflect.TypeOf(c).String(),     //**string
		reflect.ValueOf(a).Interface(), //a
		reflect.ValueOf(b).Interface(), //0x14000096d50
		reflect.ValueOf(c).Interface(), //0x140000aa030
	)
	_, err1 := http.Get("https://www.google.com")
	m := make(map[string]any)
	err2 := json.Unmarshal([]byte{}, &m)
	err3 := os.Remove("nofile")
	fmt.Println(
		reflect.TypeOf(err1).String(),                  //*url.Error
		reflect.TypeOf(err2).String(),                  //*json.SyntaxError
		reflect.TypeOf(err3).String(),                  //*fs.PathError
	)
}