逐步学习Go-reflect

内容纲要

Go类型特性

  1. 静态类型:编译确定无法改变
  2. 类型推断::=
  3. 类型安全:两个类型不能直接赋值,需要类型类型断言或者转换
  4. 丰富的基本类型
  5. 强大的符合类型
  6. 指针
  7. 接口类型
  8. 类型转换
  9. 泛型: go 1.18版本以后

经典 i != j


    type MyInt int
    var i int = 0
    var j MyInt = 1
    j = i
    i = j

file

interface

interface类型(强调一下:是个类型)可以存储任何具体类型,比如:int,int32,int64, struct etc.

interface{}

这个一个空interface,这表示什么?interface <=> any。inteface{}是代表能满足任何值,因为每个值都有一个或者多个方法。

一个interface表示什么?

Go中的接口类型变量存储两个部分:变量的具体值和值的具体类型描述符。

Go反射法则

  1. 反射从接口值到反射对象(Reflection goes from interface value to reflection object.)。
  2. 反射从反射对象到反射值。
  3. 要修改反射对象,那么这个值必须是可设置的(settable)

反射从接口值到反射对象

这条法则表示要从街口值获取反射对象。要使用反射来操作就得先获取这个值的接口对象。

在go中,接口值由两部分组成:类型信息和值信息。反射对象包含了接口值的类型信息和值信息,还有一写方法和函数查询和操作这些信息。

反射基础:

  1. reflect.Typ
  2. reflect.Value

编码来试下:


var x int32 = 4
fmt.Println("type:", reflect.TypeOf(x))

反射过程可以从反向对象到接口值

这个法则是说:使用reflect.ValueOf函数可以获取到一个反射对象,然后可以通过反射对象的方法将其转换为具体的值。

举个例子:


func TestReflect_Law2(t *testing.T) {
    var x int = 10
    // 获取反射值对象
    v := reflect.ValueOf(x)
    if v.Kind() == reflect.Int {
        // 将值对象转换为interface
        interfaceValue := v.Interface()

        // 使用go的类型断言转换为具体类型的值
        value := interfaceValue.(int)

        // 像普通值一样传递给其他函数或者方法
        // 输出: Value: 10
        fmt.Println("Value:", value)
    }

}

要修改反射对象,那么这个值必须是可设置的(settable)

可设置需要满足的条件

  1. 值是指针,并且指向一个可修改的对象
  2. 值是可修改的

基本上就是要修改反射对象,那么这个对象对应值的描述得是指针,不能是常量有const描述符。
这个遵循Go中参数传递规则:你给函数传一个指针那这个指针在函数内被修改了可以传出;如果你传入一个值,那么在函数内修改了无法传出,因为是值是被复制传入了函数。

可以修改的举例:


func TestReflect_Law3_ShouldSettable_WhenSatisfyConditions(t *testing.T) {
    // 条件2: 变量而非常量
    x := 10
    // 条件1:指针
    v := reflect.ValueOf(&x)
    // 获取指针指向的值的反射对象
    v = v.Elem()

    // 修改值为 20
    if v.CanSet() {
        v.SetInt(20)
        // 输出: x: 20
        fmt.Println("x:", x)
    }

    assert.Equal(t, 20, x)
}

不可以修改的举例:


func TestReflect_Law3_ShouldNotSettable_WhenValueIsConstVariable(t *testing.T) {
    // 违反条件2:变量而不是常量,这里定义了常量
    const x int = 10
    // 违反条件2:指针,这里是值
    v := reflect.ValueOf(x)

    if v.CanSet() {
        // 不会执行到这里,因为常量是不可修改的
        fmt.Println("x is settable")
        // 修改值为 20
        v.SetInt(20)
    }

    assert.Equal(t, 10, x)
}

func TestReflect_Law3_ShouldNotSettable_WhenValueIsNotPointer(t *testing.T) {
    // 满足条件2:变量而非常量
    x := 10
    // 违反条件1:指针,这里是值
    v := reflect.ValueOf(x)

    if v.CanSet() {
        // 不会执行到这里,因为非指针类型的值是不可修改的
        fmt.Println("x is settable")
        // 修改值为 20
        v.SetInt(20)
    }
    assert.Equal(t, 10, x)
}

三个法则说明了啥?

第一法则表明,可以从任何接口值获取三样东西中的任意一个或全部:接口值的类型(通过 reflect.TypeOf)、接口值的种类(通过 reflect.Value.Kind())和接口值本身(通过 reflect.ValueOf)。

根据第二法则,你可以通过反射对象(reflect.Value)将其转换回接口值(interface{}),这是通过调用 reflect.Value 的 Interface() 方法完成的。之后,如果你知道原始类型,你可以将 interface{} 类型通过类型断言转换回具体类型。

第三法则涉及修改变量。只有那些“可设置(settable)”的 reflect.Value 可以被改变。在反射的上下文中,"可设置" 指的是 reflect.Value 对象是否可以修改对应的值。

反射应用场景?

新建结构对象/示例

reflect.New():
file

举个例子

场景:你要开发一个代理,你提供了几个内置的负载均衡算法:Roundrobin,Random Weight,它们的负载均衡缩写分别为:rr, weight。用户在配置代理时可以通过rrweight字符串来指定负载均衡算法, 比如:


# Roundrobin 负载均衡
proxy:
  uri: /foo/**
  upstreams:
    lb: rr
    targets:
      - http://app1:8080
      - http://app2:8080
      - http://app3:8080

# Weight 负载均衡
proxy:
  uri: /foo/**
  upstreams:
    lb: rr
    targets:
      - host: http://app1:8080
        weight: 10
      - host: http://app1:8080
        weight: 10
      - host: http://app1:8080
        weight: 10

然后你定义了一个interface和struct来实现这两个负载均衡算法

type Target struct {
    Target string
    Weight int
}

// 定义一个负载均衡器接口类型
type Balancer interface {
    Elect(hosts []*Target) (*Target, error)
}

// Roundrobin实现
type RoundrobinBalancer struct {}
....

// random weight 实现
type WeightBalancer struct {}
...

使用reflect来创建负载均衡器:


var(
    lbRegistry = make(map[string]reflect.Type)
)

// 使用init方法来初始化
func init() {
    lbRegistry['rr'] = reflect.TypeOf(RoundrobinBalancer{})
    lbRegistry['weight'] = reflect.TypeOf(WeightBalancer{})
}

// 根据lbRegistry创建负载均衡
func New(blance string) (Balancer, error) {
    alg, ok := lbRegistry[balance]
    if !ok {
        return nil, errors.New("Unsupported load balancer algorithm")
    }

    return reflect.New(alg).Elem().Addr().interface().(Balancer), nil
}

json/yaml文件序列化与反序列化(动态绑定)

参考

  1. Go Data Structures: Interfaces
  2. The Laws of Reflection

发表评论

您的电子邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部