第十节 Go的断言、反射和Context

 

断言、反射和Context

与其说是教程,更像是总结

一起学习:smiley:


断言

断言的基本概念

把一个接口类型的东西,断言为他的原始结构,并且可以调用原始结构的方法和属性

/**
  @Go version: 1.17.6
  @project: TeachCode
  @ide: GoLand
  @file: TEST.go
  @author: Lido
  @time: 2022-03-05 22:33
  @description: 断言
*/
package main

import "fmt"

type User struct {
	name string
	age  int
	sex  int
}

type Student struct {
	User
}

func (u User) SayHello() {
	fmt.Println(u.name,u.sex,u.age)
}

func main() {
	user := User{
		name: "lihua",
		age:  1,
		sex:  2,
	}

	___check(user)
}

func ___check(v interface{}) {
    //断言,确定v为User类型
	v.(User).SayHello()
}
lihua 1 2

断言的应用

/**
  @Go version: 1.17.6
  @project: TeachCode
  @ide: GoLand
  @file: TEST.go
  @author: Lido
  @time: 2022-03-05 22:33
  @description: 断言
*/
package main

import "fmt"

type User struct {
	name string
	age  int
	sex  int
}

type Student struct {
	User
}

func (u User) SayHello(name string) {
	fmt.Println(name)
}

func main() {
	user := User{
		name: "lihua",
		age:  1,
		sex:  2,
	}
	Check(user)

	s := Student{User{}}
	Check(s)
}

func Check(v interface{}) {
	//用判断来判断类型
	switch v.(type) {
	case User:
		fmt.Printf("%T is User\n",v)
	case Student:
		fmt.Printf("%T is Student\n",v)
	}
}
main.User is User
main.Student is Student

反射

反射的基本概念

反射就是解决大量的断言判断修改

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	name string
	age  int
	sex  int
}

type Student struct {
	User
}

func (u User) SayHello(name string) {
	fmt.Println(name)
}

func main() {
	user := User{
		name: "liuhua",
		age:  1,
		sex:  2,
	}
	Check(user)

	s := Student{User{}}
	Check(s)
}

func Check(i interface{}) {
	//判断类型
	t := reflect.TypeOf(i)//直接反射出i的类型和值
	v := reflect.ValueOf(i)

	fmt.Println(t, v)
}
main.User {lihua 1 2}
main.Student 0

取值

func check(i interface{}) {
   //判断类型
   t := reflect.TypeOf(i)
   v := reflect.ValueOf(i)

   for i := 0; i < t.NumField(); i++ {
      fmt.Println(v.Field(i))
   }
}
lihua
1
2

多级取值

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	name string
	age  int
	sex  int
}

type Student struct {
	classroom string
	User
}

func main() {
	s := Student{
		"software engineering",
		User{
			name: "lihua",
			age:  1,
			sex:  2,
		},
	}
	Check(s)
}

func Check(i interface{}) {
	//判断类型
	t := reflect.TypeOf(i)
	v := reflect.ValueOf(i)

	fmt.Println(t, v)

	//取多层级 取出第一层中的第二个元素
	fmt.Println(v.FieldByIndex([]int{0}))
	fmt.Println(v.FieldByIndex([]int{1, 0}))
	fmt.Println(v.FieldByIndex([]int{1, 1}))
	fmt.Println(v.FieldByIndex([]int{1, 2}))
}
main.Student {software engineering {lihua 1 2}}
software engineering
lihua
1
2

fmt.Println(v.FieldByName("classroom"))

//software engineering

判断类型

func check(i interface{}) {
   //判断类型
   t := reflect.TypeOf(i

   ty := t.Kind()
   if ty == reflect.Struct {
      fmt.Println("I'm a Struct")
   }
}
I'm a Struct

修改

package main

import (
   "reflect"
)

type User struct {
   name string
   age  int
   sex  int
}

type Student struct {
   Classroom string
   User
}

func main() {
   s := Student{
      "software engineering",
      User{
         name: "lihua",
         age:  1,
         sex:  2,
      },
   }
   //要修改就要传入地址
   change(&s)
}

func change(i interface{}) {
   //判断类型
   v := reflect.ValueOf(i)                       //&{software engineering {lihua 1 2}}
   e := v.Elem()                                 //{software engineering {lihua 1 2}}
   e.FieldByName("Classroom").SetString("计算机科学") //&{计算机科学 {lihua 1 2}}
}

调用结构体中方法

package main

import (
	"fmt"
	"reflect"
)

type User struct {
	name string
	age  int
	sex  int
}

type Student struct {
	Classroom string
	User
}

func (u User) SayHello(name string) {
	fmt.Println("hello ", name)
}

func (u User) SayBye(name string) {
	fmt.Println("bye ", name)
}

func (u User) SayAye(name string) {
	fmt.Println("aye ", name)
}

func (u User) SayZye(name string) {
	fmt.Println("zye ", name)
}

func main() {
	user := User{
		name: "lido",
		age:  1,
		sex:  2,
	}

	//要修改就要传入地址
	change(user)
}

func change(i interface{}) {
	//判断类型
	v := reflect.ValueOf(i)
	//spring IOC类似的操作
	m := v.Method(0)//按顺序调用
	m.Call([]reflect.Value{reflect.ValueOf("你好")})
	m = v.Method(1)
	m.Call([]reflect.Value{reflect.ValueOf("你好")})
	m = v.Method(2)
	m.Call([]reflect.Value{reflect.ValueOf("你好")})
	m = v.Method(3)
	m.Call([]reflect.Value{reflect.ValueOf("你好")})
}

Context

普通协程

package main

import (
	"fmt"
	"time"
)

func main() {
	flag := make(chan bool)
	messages := make(chan int)

	//需要先打开管道
	go son(flag, messages)

	for i := 0; i < 3; i++ {
		//再发送值
		messages <- i
	}

	fmt.Println("主进程结束")
}

func son(flag chan bool, msg chan int) {
	t := time.Tick(time.Second)
	for _ = range t {
		select {
		case <-time.After(2 * time.Second):
			fmt.Println("timeout")
			return
		case m := <-msg:
			fmt.Printf("接收到的值: %d\n", m)
		}
	}
}
接收到的值: 0
接收到的值: 1
接收到的值: 2
主进程结束

context携带数据

func main() {
	//相当于暂时保存数据
	ctx := context.WithValue(context.Background(), "name", "lido")
	ctx, clear := context.WithCancel(ctx)
	messages := make(chan int)
	go son(ctx, messages)
	for i := 0; i < 3; i++ {
		messages <- i
	}
	clear()//配合 case <-ctx.Done():
	time.Sleep(time.Second)
	fmt.Println("主进程结束")
}

func son(ctx context.Context, msg chan int) {
	t := time.Tick(time.Second)
	for _ = range t {
		select {
		case m := <-msg:
			fmt.Printf("接收到的值: %d\n", m)
		case <-ctx.Done():
			fmt.Println("我结束了", ctx.Value("name"))
			return
		}
	}
}
接收到的值: 0
接收到的值: 1
接收到的值: 2
我结束了 value!
主进程结束

设置超时时间

结束日期和超时时间

package main

import (
	"context"
	"fmt"
	"time"
)

func main() {
    //设置3秒超时
	ctx, claer := context.WithTimeout(context.Background(), time.Second*3) 
    //设置结束日期
    //ctx, claer := context.WithDeadline(context.Background(), time.Now().Add(time.Second*3))
	messages := make(chan int)
	go son(ctx, messages)
	//发送5个值
	for i := 0; i < 5; i++ {
		messages <- i
	}
	claer()
	time.Sleep(time.Second)
	fmt.Println("主进程结束")
}

func son(ctx context.Context, msg chan int) {
	//间隔接收
	t := time.Tick(time.Second)
	for _ = range t {
		select {
		case m := <-msg:
			fmt.Printf("接收到的值: %d\n", m)
		case <-ctx.Done():
			fmt.Println("我结束了")
			return
		}
	}
}
接收到的值: 0
接收到的值: 1
接收到的值: 2
我结束了

案例

模拟信息监控

package main

import (
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup
var stop chan bool = make(chan bool)

func cpuInfo() {
	defer wg.Done()
	for {
		select {
		case <-stop:
			fmt.Println("退出信息监控")
			return
		default:
			time.Sleep(time.Second * 2) //每隔两秒读取一次
			fmt.Println("读取信息完成")
		}
	}
}

func main() {
	wg.Add(1)
	go cpuInfo()
	time.Sleep(time.Second * 6)
	stop <- true
	wg.Wait()
	fmt.Println("信息监控完成")
}
读取信息完成
读取信息完成
读取信息完成
退出信息监控
信息监控完成

使用context的cancel后

package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func Info(ctx context.Context) {
	defer wg.Done()
	for {
		select {
		case <- ctx.Done()://替换了 case <-stop
			fmt.Println("退出监控")
			return
		default:
			time.Sleep(time.Second) //每隔一秒读取一次
			fmt.Println("读取信息完成")
		}
	}
}

func main() {
	wg.Add(1)
	ctx, cancel := context.WithCancel(context.Background())
	go Info(ctx)
	time.Sleep(time.Second * 6)
	cancel()//代替 stop <- true
	wg.Wait()
	fmt.Println("信息监控完成")
}
读取信息完成
读取信息完成
读取信息完成
读取信息完成
读取信息完成
读取信息完成
退出监控
信息监控完成

context的多路监听

package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func cpuInfo(ctx context.Context) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			fmt.Println("退出CPU监控")
			return
		default:
			time.Sleep(time.Second) //每隔一秒读取一次
			fmt.Println("CPU读取信息完成")
		}
	}
}

func memoryInfo(ctx context.Context) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			fmt.Println("退出内存监控")
			return
		default:
			time.Sleep(time.Second) //每隔一秒读取一次
			fmt.Println("内存读取信息完成")
		}
	}
}

func main() {
	wg.Add(1)
	ctx, cancel := context.WithCancel(context.Background())
	go cpuInfo(ctx)
	go memoryInfo(ctx)
	time.Sleep(time.Second * 3)
	cancel()
	wg.Wait()
	fmt.Println("CPU信息监控完成")
}

父子嵌套

package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func cpuInfo(ctx context.Context) {
	defer wg.Done()
	ctx2, _ := context.WithCancel(ctx) //使用父节点
	go memoryInfo(ctx2)                //调用父节点,当父节点被取消时,子节点也会跟着一起被取消
	for {
		select {
		case <-ctx.Done():
			fmt.Println("退出CPU监控")
			return
		default:
			time.Sleep(time.Second) //每隔一秒读取一次
			fmt.Println("CPU读取信息完成")
		}
	}
}

func memoryInfo(ctx context.Context) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			fmt.Println("退出内存监控")
			return
		default:
			time.Sleep(time.Second) //每隔一秒读取一次
			fmt.Println("内存读取信息完成")
		}
	}
}

func main() {
	wg.Add(2)
	ctx, cancel := context.WithCancel(context.Background())
	go cpuInfo(ctx) //嵌套调用ctx
	time.Sleep(time.Second * 2)
	cancel() //两秒结束后,取消ctx
	wg.Wait()
	fmt.Println("信息监控完成")
}

其他context,也可以自己去实现他的接口

ctx,_ := context.WithCannel(ctx)   //需要取消操作的ctx
ctx,_ := context.WithDeadline(ctx) //到点停止的ctx
ctx,_ := context.WithTimeout(ctx)  //超时处理
ctx,_ := context.WithValue(ctx)

使用WithTimeout()

可以接收cancel,自己调用cancel()来结束(在主协程结束之前)

也可以自动调用

package main

import (
	"context"
	"fmt"
	"sync"
	"time"
)

var wg sync.WaitGroup

func cpuInfo(ctx context.Context) {
	defer wg.Done()
	for {
		select {
		case <-ctx.Done():
			fmt.Println("退出CPU监控")
			return
		default:
			time.Sleep(time.Second) //每隔一秒读取一次
			fmt.Println("CPU读取信息完成")
		}
	}
}

func main() {
	wg.Add(1)
	ctx, _ := context.WithTimeout(context.Background(), 3*time.Second) //会自动结束,当然也可以手动cannel()
	go cpuInfo(ctx)                                                    //嵌套调用ctx
	time.Sleep(time.Second * 6)
	wg.Wait()
	fmt.Println("信息监控完成")
}