• 反射


    反射

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type Student struct {
    	name string
    }
    type MyInt int
    
    func main() {
    	/*Type的主要方法
    	Kind()返回一个常量,表示具体类型的底层类型
    	Elem()返回指针、数组、切片、字典、通道等类型
    	*/
    	/*Value的主要方法
    	Kind()返回一个常量,表示具体类型的底层类型
    	Type()返回具体类型所对应的reflect.Type(静态类型)
    	*/
    	/*
    		在go语言中,静态类型就是变量声明时赋予的类型,也就是在发射中reflect.Type对应的值,而Kind()对应的是基础类型。
    		Kind()大概会返回切片、字典、指针、结构体、接口、字符串、数组、函数、整形或其他基础类型。如下面代码中Kind()
    		返回结构体:fmt.Println(p.Kind()),而Type()返回静态类型名Student:fmt.Println(p.Type()).MyInt是静态类型
    		而int是它的基础类型。
    		Type()返回的是静态类型,而Kind()返回的是基础类型
    	*/
    	var a int = 9
    	v := reflect.ValueOf(a)                         //返回Value类型对象,值为9
    	t := reflect.TypeOf(a)                          //返回Type类型对象,值为int
    	fmt.Println(v, t, v.Type(), v.Kind(), t.Kind()) //Kind()返回底层基础类型
    	/*
    		9 int int int int
    	*/
    
    	var mi MyInt = 99
    	mv := reflect.ValueOf(mi)                            //返回Value类型对象,值为99
    	mt := reflect.TypeOf(mi)                             // 返回Type类型对象,值为MyInt
    	fmt.Println(mv, mt, mv.Type(), mv.Kind(), mt.Kind()) //Kind()返回底层基础类型
    	/*
    		99 main.MyInt main.MyInt int int
    	*/
    
    	var b [5]int = [5]int{5, 6, 7, 8}
    	fmt.Println(reflect.TypeOf(b), reflect.TypeOf(b).Kind(), reflect.TypeOf(b).Elem())
    	/*
    		[5]int array int
    	*/
    
    	var Pupil Student
    	p := reflect.ValueOf(Pupil) //使用ValueOf获得结构体的Value对象
    	fmt.Println(p.Type(), p.Kind())
    	/*
    		main.Student struct
    	*/
    
    }
    
    

    反射的应用

    /*反射的应用
    	(1)通过反射可以修改对象
    	通过反射可以修改对象,但对象必须是可寻址的(addressable)。简单说,如果想通过反射修改对象,就需要把想修改
    	对象的指针传递过来。如果对象不能被寻址,那就是不可写的。可写性是反射类型变量的一个属性,但不是所有反射类型
    	变量都拥有这个属性,所以通过反射修改原对象,需要判断其可写性,也就是可寻址。
    
    	实际上要修改的是指针指向的数据,需要调用Value类型的Elem()方法。Elem()方法能够对指针进行间接引用,将结果
    	存储到reflect.Value类型对象中。
    	v := reflect.Value.Elem() //表示获取原始值对应的反射对象
    
    	通过CanSet()方法来判断原始反射对象v reflect.Value是否可写,CanAddr()方法判断它是否可被取地址。这里的v是通过
    	Elem()得到的。CanSet()和CanAddr()这两个方法的签名如下:
    	func (v Value)CanAddr()
    	func (v Value)CanSet() bool
    	*/
    
    	var a int = 9
    	v := reflect.ValueOf(a) // 返回Value类型对象,值为9
    	t := reflect.TypeOf(a)  // 返回Type类型对象,值为int
    
    	fmt.Println(v.Type(), t.Kind(), reflect.ValueOf(&a).Elem())
    	/*
    		int int 9
    	*/
    	fmt.Println(reflect.ValueOf(a).CanSet(), reflect.ValueOf(a).CanAddr())
    	/*
    		false false
    	*/
    	fmt.Println(reflect.ValueOf(&a).CanSet(), reflect.ValueOf(&a).CanAddr())
    	/*
    		false false
    	*/
    
    	pa := reflect.ValueOf(&a).Elem() // reflect.Value.Elem()表示获取原始值对应的反射对象
    	fmt.Println(pa.CanSet(), pa.CanAddr())
    	/*
    		true true
    	*/
    
    	pa.SetInt(100)
    	fmt.Println(pa)
    	/*
    		100
    	*/
    
    	var Pupil Student = Student{"jim", 8}
    	Pupilv := reflect.ValueOf(Pupil) //使用ValueOf()获取结构体的Value对象
    	fmt.Println(Pupilv.Type(), Pupilv.Kind())
    	/*
    		main.Student struct
    	*/
    
    	p := reflect.ValueOf(&Pupil).Elem() //获取原始值对应的反射对象
    	fmt.Println(p.CanSet(), p.CanAddr())
    	/*
    		true true
    	*/
    
    	// p.Field(0).SetString("Mike") 报错,原因是字段需要大写(需可以被外部包调用)
    	p.Field(1).SetInt(10)
    	fmt.Println(p)
    	/*
    		{jim 10}
    	*/
    
    	/*
    		要通过反射的方式来修改对象,重点是通过方法Elem()获取原始值对应的反射对象。虽然反射可以越过go语言的导出规则
    		的限制读取结构中未导出的成员,但不能修改他们。因为一个结构体中只有被导出(可被外部包调用)的字段才是可写的。
    		reflect.ValueOf(&a)得到的是原始变量a的指针地址,这个指针地址再通过Elem()方法得到反射对象。
    	*/
    
    	/*
    		结构体中有tag标签,通风反射可获取结构体成员变量的tag信息。
            type Student struct {
                    name string
                    Age  int `json:"years"`
             }
    	*/
            
    	var s Student = Student{"joke", 18}
    	setStudent := reflect.ValueOf(&s).Elem()
    
    	sSAge, _ := setStudent.Type().FieldByName("Age")
    	fmt.Println(sSAge.Tag.Get("json"))
    	/*
    		years
    	*/
    
    	/*
    		(2)通过反射可以创建基础类型和用户自定义类型变量
    		除了可以通过反射创建基础类型和用户自定义类型,还可以使用反射来创建切片,字典,通道,甚至包括函数类型。常见函数有:
    		reflect.Makeslice(),reflect.Makemap()和reflect.Makechan()
    	*/
    	/*
    		想创建变量,需要先确定类型。下面的代码中根据reflect.Type(t)得到t的静态类型,接着使用reflect.New(vartype)生成了新变量。
    		新变量通过方法Elem()获取的反射对象来设置变量值。最后使用Elem().interface()来反引用reflect的指针,得到新变量的值。
    	*/
    
            /*
    	t := 9
    	// 反射创建int变量
    	varType := reflect.TypeOf(t)
    
    	v1 := reflect.New(varType)
    	v1.Elem().SetInt(1)
    	varNew := v1.Elem().Interface()
    	fmt.Printf("指针:%d, 值:%d\n", v1, varNew)
    
    	// 反射创建map slice
    	newSlice := make([]int, 5)
    	newmap := make(map[string]int)
    	sliceType := reflect.TypeOf(newSlice)
    	mapType := reflect.TypeOf(newmap)
    
    	// 创建新值
    	ReflectSlice := reflect.MakeSlice(sliceType, 5, 5)
    	Reflectmap := reflect.MakeMap(mapType)
    
    	// 使用新创建的变量
    	V := 99
    	SliceV := reflect.ValueOf(V)
    	ReflectSlice = reflect.Append(ReflectSlice, SliceV)
    	intSlice := ReflectSlice.Interface().([]int)
    	fmt.Println("Slice:", intSlice)
    
    	Key := "Rose"
    	Value := 999
    	MapKey := reflect.ValueOf(Key)
    	MapValue := reflect.ValueOf(Value)
    	Reflectmap.SetMapIndex(MapKey, MapValue)
    	mapStringInt := Reflectmap.Interface().(map[string]int)
    	fmt.Println("Map:", mapStringInt)
    	/*
    		指针:824633811096, 值:1
    		Slice: [0 0 0 0 0 99]
    		Map: map[Rose:999]
    	*/
    }
    

    通过反射机制,能对一个结构体类型的大致结构如方法、字段的情况有较为全面的了解

    package main
    
    import (
    	"fmt"
    	"reflect"
    )
    
    type ss struct {
    	int
    	string
    	bool
    	float64
    }
    
    func (s ss) Method(i int) string   { return "结构体方法1" }
    func (s *ss) Method2(i int) string { return "结构体方法2" }
    
    var (
    	structValue = ss{ // 结构体
    		20,
    		"结构体",
    		false,
    		64.0,
    	}
    )
    
    func main() {
    	// 通过反射机制,能对一个结构体类型的大致结构如方法、字段的情况有较为全面的了解
    	fmt.Println("-------------引用------------")
    	v := reflect.ValueOf(&structValue)
    	fmt.Println(v.String())  // 反射值的字符串形式
    	fmt.Println(v.Type())    //反射值的类型
    	fmt.Println(v.Kind())    //反射值的类别
    	fmt.Println(v.CanAddr()) //是否可以获取地址
    	fmt.Println(v.CanSet())  //是否可以修改
    	if v.CanAddr() {
    		fmt.Println(v.Addr())       //获取地址
    		fmt.Println(v.UnsafeAddr()) // 获取自由地址
    	}
    	// 获取方法数量
    	fmt.Println("可用方法数量:", v.NumMethod())
    	if v.NumMethod() > 0 {
    		i := 0
    		for ; i < v.NumMethod()-1; i++ {
    			fmt.Println(v.Method(i).String())
    		}
    		fmt.Println(v.Method(i).String())
    		// 通过名称获取方法
    		fmt.Println(v.MethodByName("Method1").String())
    		fmt.Println(v.MethodByName("Method2").String())
    	}
    
    	fmt.Println("------------------值变量------------------")
    	v = reflect.ValueOf(structValue)
    	fmt.Println(v.String())  //反射值的字符串形式
    	fmt.Println(v.Type())    // 反射值的类型
    	fmt.Println(v.Kind())    // 反射值的类别
    	fmt.Println(v.CanAddr()) //是否可以获取地址
    	fmt.Println(v.CanSet())  //是否可以修改
    	if v.CanAddr() {
    		fmt.Println(v.Addr())       //获取地址
    		fmt.Println(v.UnsafeAddr()) //获取自由地址
    	}
    	// 获取方法数量
    	fmt.Println(v.NumMethod())
    	fmt.Println(reflect.ValueOf(&structValue).NumMethod())
    	if v.NumMethod() > 0 {
    		i := 0
    		for ; i < v.NumMethod()-1; i++ {
    			fmt.Println(v.Method(i).String())
    		}
    		fmt.Println(v.Method(i).String())
    		// 通过名称获取方法
    		fmt.Println(v.MethodByName("Method1").String())
    		fmt.Println(v.MethodByName("Method2").String())
    	}
    
    	switch v.Kind() {
    	// 结构体
    	case reflect.Struct:
    		fmt.Println("-----------结构体----------")
    		// 获取字段个数
    		fmt.Println(v.NumField())
    		if v.NumField() > 0 {
    			var i int
    			//遍历结构体字段
    			for i = 0; i < v.NumField()-1; i++ {
    				field := v.Field(i) //获取结构体字段
    				fmt.Println(field.Type(), field.String())
    			}
    			field := v.Field(i) // 获取结构体字段
    			fmt.Println(field.Type(), field.String())
    			// 通过名称查找字段
    			if v := v.FieldByName("ptr"); v.IsValid() {
    				fmt.Println(v.Type().Name())
    			}
    			// 通过函数查找字段
    			v := v.FieldByNameFunc(func(s string) bool { return len(s) > 3 })
    			if v.IsValid() {
    				fmt.Println(v.Type().Name())
    			}
    		}
    	}
    }
    
    /*
    -------------引用------------
    <*main.ss Value>
    *main.ss
    ptr
    false
    false
    可用方法数量: 2
    <func(int) string Value>
    <func(int) string Value>
    <invalid Value>
    <func(int) string Value>
    ------------------值变量------------------
    <main.ss Value>
    main.ss
    struct
    false
    false
    1
    2
    <func(int) string Value>
    <invalid Value>
    <invalid Value>
    -----------结构体----------
    4
    int <int Value>
    string 结构体
    bool <bool Value>
    float64 <float64 Value>
    */
    
  • 相关阅读:
    [九、完整项目]3完成首页顶部内容的制作
    [九、完整项目]10完成课程列表视图的制作
    [九、完整项目]7将四个功能模块合并成首页
    [九、完整项目]4完成首页课程类别列表的制作
    [九、完整项目]8完成课程类型和课程列表类型的定义
    [九、完整项目]2集中存放应用程序所有视图通用的修饰符
    [九、完整项目]6完成首页热门主题列表的制作
    [九、完整项目]9完成历史课程列表的制作
    [九、完整项目]1使用Swift UI技术搭建教程合集的界面
    [九、完整项目]5完成首页课程班级列表的制作
  • 原文地址:https://www.cnblogs.com/weiweivip666/p/16141926.html
Copyright © 2020-2023  润新知