反射和类型断言

反射和类型断言

1.反射简介

反射机制:在编译时不知道类型的情况下,可更新变量、在运行时查看值、调用方法以及直接对它们的布局进行操作,反射也让我们可以把类型当做头等值

  • 通过反射获取类型信息

在 Go 语言中通过调用 reflect.TypeOf 函数,我们可以从一个任何非接口类型的值创建一个 reflect.Type 值。reflect.Type 值表示着此非接口值的类型。通过此值,我们可以得到很多此非接口类型的信息。当然,我们也可以将一个接口值传递给一个 reflect.TypeOf 函数调用,但是此调用将返回一个表示着此接口值的动态类型的 reflect.Type 值。

实际上,reflect.TypeOf 函数的唯一参数的类型为 interface{},reflect.TypeOf 函数将总是返回一个表示着此唯一接口参数值的动态类型的 reflect.Type 值。

package main

import (
	"fmt"
	"reflect"
)

func main(){
	//空接口可以存储任意类型数据   空接口类型数据无法计算
	var i interface{} = 123
	//var j interface{} = 456
	//fmt.Println(i + j)  无法进行计算

	t1 := reflect.TypeOf(i)

	fmt.Printf("%T\n",t1)
	fmt.Println(t1)
}
C:\Users\vSphere\go\src\day04>go run 06.go
*reflect.rtype
int
  • 接口数据类型进行计算
package main

import (
	"fmt"
	"reflect"
)

func main(){
	//空接口可以存储任意类型数据   空接口类型数据无法计算
	var i interface{} = 123
	var j interface{} = 456
	//fmt.Println(i + j)  无法进行计算

	t1 := reflect.TypeOf(i)
	t2 := reflect.TypeOf(j)

	fmt.Printf("%T\n",t1)
	fmt.Println(t1)

	if t1 == t2 {
		v1 := reflect.ValueOf(i).Int()
		v2 := reflect.ValueOf(j).Int()
		fmt.Println(v1 + v2)
	}
}
C:\Users\vSphere\go\src\day04>go run 06.go
*reflect.rtype
int
579
  • 函数: reflect.ValueOf().Int()
package main

import (
	"fmt"
	"reflect"
)

func main(){
	//空接口可以存储任意类型数据   空接口类型数据无法计算
	var i interface{} = 123
	var j interface{} = 456
	//fmt.Println(i + j)  无法进行计算

	t1 := reflect.TypeOf(i)
	t2 := reflect.TypeOf(j)

	fmt.Printf("%T\n",t1)
	fmt.Println(t1)

	if t1 == t2 {
		v1 := reflect.ValueOf(i).Int()
		v2 := reflect.ValueOf(j).Int()
		fmt.Println(v1 + v2)

		fmt.Println(v1)
		fmt.Printf("%T\n",v1)
	}
}
C:\Users\vSphere\go\src\day04>go run 06.go
*reflect.rtype
int
579
123
int64

2.reflect.Type

Type 表示 go 语言的一个类型,它是一个有很多方法的接口,这些方法可以用来识别类型以及透视类型的组成部分,比如一个结构的各个字段或者一个函数的各个参数

reflect.Type 接口只有一个实现,即类型描述符,接口值中的动态类型也是类型描述符

  • Reflect.Type 函数接受任何的 interface{}参数,并且把接口中的动态类型以 reflect.Type 形式返回
t := reflect.TypeOf(3) //一个reflect.Type
//上面的TypeOf(3)调用把数值3赋给interface{}参数,把一个具体值赋给一个接口类型时发生一个隐式类型转换,转换会生成一个包含两部分内容的接口值:动态类型部分三操作数的值
  • 因为 reflect.TypeOf 返回一个接口值对应的动态类型,所以它返回总是具体类型,而不是接口类型
var w io.Writter = os.Stdout
fmt.Println(reflect.TypeOf(w)) //*os.File而不是io.Writer

注意,reflect.Type 满足 fmt.Stringer,因为输出一个接口值的动态类型在调试和日志中很常用

3.reflect.Value

reflect.Value 可以包含一个任意类型的值

reflect.ValueOf 函数接受任意的 interface{}并将接口的动态值以 reflect.Value 的形式返回,与 reflect.TypeOf 类似,reflect.ValueOf 的返回值也都是具体值,不过 reflect.Value 也可以包含一个接口值.

v := reflect.ValueOf(3) //一个reflect.Value
fmt.Println(v)          //3
fmt.Println(v.String()) //int
  • 另一个与 reflect.Type 类似的是,reflect.Value 也满足 fmt.Stringer,但除非 value 包含的是一个字符串,否则 string 方法的结果仅仅暴露了类型
//调用value的type方法会把它的类型以reflect.Type方式返回
t := v.Type()
fmt.Println(t.String()) //int
  • reflect.ValueOf 的逆操作是 reflect.Value.Interface 方法,它返回一个 interface{}接口,与 reflect.Value 包含同一个具体值
v := reflect.ValueOf(3) // a reflect.Value
x := v.Interface()      //an  interface{}
i := x.(int)            //an int
fmt.Printf("%d\n", i)

reflect.Value 和 interface()都可以包含任意的值,二者的区别是空接口隐藏了值的布局信息\内置操作和相关方法,所以除非我们知道它的动态类型,并用一个类型断言来渗透进去,否则我们对包含值能做的事情很少

4.通用的格式化函数

不用类型分支,我们用 reflect.Value 的 Kind 方法来区分不同的类型,尽管有无限种类型,但类型的分类只有少数几种:基础类型 Bool,String 以及各种数字类型;聚合类型 Array 和 Struct;引用类型 Chan,Func,Ptr,Slice 和 Map,接口类型 interface;最后还有 Invalid 表示它还没有任何值

// Any formats any value as a string.
func Any(value interface{}) string {
	return formatAtom(reflect.ValueOf(value))
}

// formatAtom formats a value without inspecting its internal structure.
func formatAtom(v reflect.Value) string {
	switch v.Kind() {
	case reflect.Invalid:
		return "invalid"
	case reflect.Int, reflect.Int8, reflect.Int16,
		reflect.Int32, reflect.Int64:
		return strconv.FormatInt(v.Int(), 10)
	case reflect.Uint, reflect.Uint8, reflect.Uint16,
		reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return strconv.FormatUint(v.Uint(), 10)
	// ...floating-point and complex cases omitted for brevity...
	case reflect.Bool:
		return strconv.FormatBool(v.Bool())
	case reflect.String:
		return strconv.Quote(v.String())
	case reflect.Chan, reflect.Func, reflect.Ptr, reflect.Slice, reflect.Map:
		return v.Type().String() + " 0x" +
			strconv.FormatUint(uint64(v.Pointer()), 16)
	default: // reflect.Array, reflect.Struct, reflect.Interface
		return v.Type().String() + " value"
	}
}

该函数把每个值当做一个没有内部结构且不可分割的物体,对于聚合类型以及接口,它只输出了值的类型,对于引用类型(通道,函数,指针,slice,map),它输出了类型和以十六进制表示的引用地址

改进,Display 递归的值显示器

调试工具函数 Display,这个函数对给定的任意一个复杂值 x,输出这个复杂值的完整结构,并对找到的每个元素标上这个元素的路径

//!+Display

func Display(name string, x interface{}) {
	fmt.Printf("Display %s (%T):\n", name, x)
	display(name, reflect.ValueOf(x))
}

//!-Display

// formatAtom formats a value without inspecting its internal structure.
// It is a copy of the the function in gopl.io/ch11/format.
func formatAtom(v reflect.Value) string {
	switch v.Kind() {
	case reflect.Invalid:
		return "invalid"
	case reflect.Int, reflect.Int8, reflect.Int16,
		reflect.Int32, reflect.Int64:
		return strconv.FormatInt(v.Int(), 10)
	case reflect.Uint, reflect.Uint8, reflect.Uint16,
		reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		return strconv.FormatUint(v.Uint(), 10)
	// ...floating-point and complex cases omitted for brevity...
	case reflect.Bool:
		if v.Bool() {
			return "true"
		}
		return "false"
	case reflect.String:
		return strconv.Quote(v.String())
	case reflect.Chan, reflect.Func, reflect.Ptr,
		reflect.Slice, reflect.Map:
		return v.Type().String() + " 0x" +
			strconv.FormatUint(uint64(v.Pointer()), 16)
	default: // reflect.Array, reflect.Struct, reflect.Interface
		return v.Type().String() + " value"
	}
}

//!+display
func display(path string, v reflect.Value) {
	switch v.Kind() {
	case reflect.Invalid:
		fmt.Printf("%s = invalid\n", path)
	case reflect.Slice, reflect.Array:
		for i := 0; i < v.Len(); i++ {
			display(fmt.Sprintf("%s[%d]", path, i), v.Index(i))
		}
	case reflect.Struct:
		for i := 0; i < v.NumField(); i++ {
			fieldPath := fmt.Sprintf("%s.%s", path, v.Type().Field(i).Name)
			display(fieldPath, v.Field(i))
		}
	case reflect.Map:
		for _, key := range v.MapKeys() {
			display(fmt.Sprintf("%s[%s]", path,
				formatAtom(key)), v.MapIndex(key))
		}
	case reflect.Ptr:
		if v.IsNil() {
			fmt.Printf("%s = nil\n", path)
		} else {
			display(fmt.Sprintf("(*%s)", path), v.Elem())
		}
	case reflect.Interface:
		if v.IsNil() {
			fmt.Printf("%s = nil\n", path)
		} else {
			fmt.Printf("%s.type = %s\n", path, v.Elem().Type())
			display(path+".value", v.Elem())
		}
	default: // basic types, channels, funcs
		fmt.Printf("%s = %s\n", path, formatAtom(v))
	}
}

go

5.reflect.Value 设置值

一个变量是一个可寻址的存储区域,其中包含了一个值,并且它的值可以通过这个地址来更新

示例,调用 reflect.ValueOf(&x).Elem()来获得任意 x 可寻址的 value 值

x := 2                  //值类型变量
a := reflect.ValueOf(2) //no
b := reflect.ValueOf(x) //no
c := reflect.Value(&x)  //no
d := x.Elem()           //2	int	yes(x)

通过变量的 CanAddr 方法来询问 reflect.Value 变量是否寻址

fmt.Println(d.CanAddr)

我们可以通过一个指针间接获取一个可寻址的 reflect.Value,从一个可寻址的 reflect.Value()获取变量需要三步.

  • 首先,调用 Addr(),返回一个 Value,其中包含一个指向变量的指针,
  • 接下来,在这个 Value 上调用 Interface(),会返回一个包含这个指针的 interface{}值
  • 最后,如果我们值这个变量的类型,可以使用类型断言把接口内容转换为一个普通指针,之后通过这个指针来更新变量
x := 2
d := reflect.ValueOf(&x).Elem()   //d代表变量x
px := d.Addr().Interface().(*int) //px:=&x
*px = 3                           //x = 3
fmt.Prinln(x)                     //3

平常由编译器来检查的那些可赋值性条件,在这种情况下则是在运行时由 Set 方法来检查,确保这个值对于类型变量是可赋值的是很重要的一件事情,在不可寻址的 reflect.Value 上调用 Set 方法也会崩溃

利用反射填充数据结构

decode.go

// Package sexpr provides a means for converting Go objects to and
// from S-expressions.
package sexpr

import (
	"bytes"
	"fmt"
	"reflect"
	"strconv"
	"text/scanner"
)

//!+Unmarshal
// Unmarshal parses S-expression data and populates the variable
// whose address is in the non-nil pointer out.
func Unmarshal(data []byte, out interface{}) (err error) {
	lex := &lexer{scan: scanner.Scanner{Mode: scanner.GoTokens}}
	lex.scan.Init(bytes.NewReader(data))
	lex.next() // get the first token
	defer func() {
		// NOTE: this is not an example of ideal error handling.
		if x := recover(); x != nil {
			err = fmt.Errorf("error at %s: %v", lex.scan.Position, x)
		}
	}()
	read(lex, reflect.ValueOf(out).Elem())
	return nil
}

//!-Unmarshal

//!+lexer
type lexer struct {
	scan  scanner.Scanner
	token rune // the current token
}

func (lex *lexer) next()        { lex.token = lex.scan.Scan() }
func (lex *lexer) text() string { return lex.scan.TokenText() }

func (lex *lexer) consume(want rune) {
	if lex.token != want { // NOTE: Not an example of good error handling.
		panic(fmt.Sprintf("got %q, want %q", lex.text(), want))
	}
	lex.next()
}

//!-lexer

// The read function is a decoder for a small subset of well-formed
// S-expressions.  For brevity of our example, it takes many dubious
// shortcuts.
//
// The parser assumes
// - that the S-expression input is well-formed; it does no error checking.
// - that the S-expression input corresponds to the type of the variable.
// - that all numbers in the input are non-negative decimal integers.
// - that all keys in ((key value) ...) struct syntax are unquoted symbols.
// - that the input does not contain dotted lists such as (1 2 . 3).
// - that the input does not contain Lisp reader macros such 'x and #'x.
//
// The reflection logic assumes
// - that v is always a variable of the appropriate type for the
//   S-expression value.  For example, v must not be a boolean,
//   interface, channel, or function, and if v is an array, the input
//   must have the correct number of elements.
// - that v in the top-level call to read has the zero value of its
//   type and doesn't need clearing.
// - that if v is a numeric variable, it is a signed integer.

//!+read
func read(lex *lexer, v reflect.Value) {
	switch lex.token {
	case scanner.Ident:
		// The only valid identifiers are
		// "nil" and struct field names.
		if lex.text() == "nil" {
			v.Set(reflect.Zero(v.Type()))
			lex.next()
			return
		}
	case scanner.String:
		s, _ := strconv.Unquote(lex.text()) // NOTE: ignoring errors
		v.SetString(s)
		lex.next()
		return
	case scanner.Int:
		i, _ := strconv.Atoi(lex.text()) // NOTE: ignoring errors
		v.SetInt(int64(i))
		lex.next()
		return
	case '(':
		lex.next()
		readList(lex, v)
		lex.next() // consume ')'
		return
	}
	panic(fmt.Sprintf("unexpected token %q", lex.text()))
}

//!-read

//!+readlist
func readList(lex *lexer, v reflect.Value) {
	switch v.Kind() {
	case reflect.Array: // (item ...)
		for i := 0; !endList(lex); i++ {
			read(lex, v.Index(i))
		}

	case reflect.Slice: // (item ...)
		for !endList(lex) {
			item := reflect.New(v.Type().Elem()).Elem()
			read(lex, item)
			v.Set(reflect.Append(v, item))
		}

	case reflect.Struct: // ((name value) ...)
		for !endList(lex) {
			lex.consume('(')
			if lex.token != scanner.Ident {
				panic(fmt.Sprintf("got token %q, want field name", lex.text()))
			}
			name := lex.text()
			lex.next()
			read(lex, v.FieldByName(name))
			lex.consume(')')
		}

	case reflect.Map: // ((key value) ...)
		v.Set(reflect.MakeMap(v.Type()))
		for !endList(lex) {
			lex.consume('(')
			key := reflect.New(v.Type().Key()).Elem()
			read(lex, key)
			value := reflect.New(v.Type().Elem()).Elem()
			read(lex, value)
			v.SetMapIndex(key, value)
			lex.consume(')')
		}

	default:
		panic(fmt.Sprintf("cannot decode list into %v", v.Type()))
	}
}

func endList(lex *lexer) bool {
	switch lex.token {
	case scanner.EOF:
		panic("end of file")
	case ')':
		return true
	}
	return false
}

//!-readlist

encode.go

package sexpr

import (
	"bytes"
	"fmt"
	"reflect"
)

//!+Marshal
// Marshal encodes a Go value in S-expression form.
func Marshal(v interface{}) ([]byte, error) {
	var buf bytes.Buffer
	if err := encode(&buf, reflect.ValueOf(v)); err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

//!-Marshal

// encode writes to buf an S-expression representation of v.
//!+encode
func encode(buf *bytes.Buffer, v reflect.Value) error {
	switch v.Kind() {
	case reflect.Invalid:
		buf.WriteString("nil")

	case reflect.Int, reflect.Int8, reflect.Int16,
		reflect.Int32, reflect.Int64:
		fmt.Fprintf(buf, "%d", v.Int())

	case reflect.Uint, reflect.Uint8, reflect.Uint16,
		reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		fmt.Fprintf(buf, "%d", v.Uint())

	case reflect.String:
		fmt.Fprintf(buf, "%q", v.String())

	case reflect.Ptr:
		return encode(buf, v.Elem())

	case reflect.Array, reflect.Slice: // (value ...)
		buf.WriteByte('(')
		for i := 0; i < v.Len(); i++ {
			if i > 0 {
				buf.WriteByte(' ')
			}
			if err := encode(buf, v.Index(i)); err != nil {
				return err
			}
		}
		buf.WriteByte(')')

	case reflect.Struct: // ((name value) ...)
		buf.WriteByte('(')
		for i := 0; i < v.NumField(); i++ {
			if i > 0 {
				buf.WriteByte(' ')
			}
			fmt.Fprintf(buf, "(%s ", v.Type().Field(i).Name)
			if err := encode(buf, v.Field(i)); err != nil {
				return err
			}
			buf.WriteByte(')')
		}
		buf.WriteByte(')')

	case reflect.Map: // ((key value) ...)
		buf.WriteByte('(')
		for i, key := range v.MapKeys() {
			if i > 0 {
				buf.WriteByte(' ')
			}
			buf.WriteByte('(')
			if err := encode(buf, key); err != nil {
				return err
			}
			buf.WriteByte(' ')
			if err := encode(buf, v.MapIndex(key)); err != nil {
				return err
			}
			buf.WriteByte(')')
		}
		buf.WriteByte(')')

	default: // float, complex, bool, chan, func, interface
		return fmt.Errorf("unsupported type: %s", v.Type())
	}
	return nil
}

//!-encode

pretty.go

package sexpr

// This file implements the algorithm described in Derek C. Oppen's
// 1979 Stanford technical report, "Pretty Printing".

import (
	"bytes"
	"fmt"
	"reflect"
)

func MarshalIndent(v interface{}) ([]byte, error) {
	p := printer{width: margin}
	if err := pretty(&p, reflect.ValueOf(v)); err != nil {
		return nil, err
	}
	return p.Bytes(), nil
}

const margin = 80

type token struct {
	kind rune // one of "s ()" (string, blank, start, end)
	str  string
	size int
}

type printer struct {
	tokens []*token // FIFO buffer
	stack  []*token // stack of open ' ' and '(' tokens
	rtotal int      // total number of spaces needed to print stream

	bytes.Buffer
	indents []int
	width   int // remaining space
}

func (p *printer) string(str string) {
	tok := &token{kind: 's', str: str, size: len(str)}
	if len(p.stack) == 0 {
		p.print(tok)
	} else {
		p.tokens = append(p.tokens, tok)
		p.rtotal += len(str)
	}
}
func (p *printer) pop() (top *token) {
	last := len(p.stack) - 1
	top, p.stack = p.stack[last], p.stack[:last]
	return
}
func (p *printer) begin() {
	if len(p.stack) == 0 {
		p.rtotal = 1
	}
	t := &token{kind: '(', size: -p.rtotal}
	p.tokens = append(p.tokens, t)
	p.stack = append(p.stack, t) // push
	p.string("(")
}
func (p *printer) end() {
	p.string(")")
	p.tokens = append(p.tokens, &token{kind: ')'})
	x := p.pop()
	x.size += p.rtotal
	if x.kind == ' ' {
		p.pop().size += p.rtotal
	}
	if len(p.stack) == 0 {
		for _, tok := range p.tokens {
			p.print(tok)
		}
		p.tokens = nil
	}
}
func (p *printer) space() {
	last := len(p.stack) - 1
	x := p.stack[last]
	if x.kind == ' ' {
		x.size += p.rtotal
		p.stack = p.stack[:last] // pop
	}
	t := &token{kind: ' ', size: -p.rtotal}
	p.tokens = append(p.tokens, t)
	p.stack = append(p.stack, t)
	p.rtotal++
}
func (p *printer) print(t *token) {
	switch t.kind {
	case 's':
		p.WriteString(t.str)
		p.width -= len(t.str)
	case '(':
		p.indents = append(p.indents, p.width)
	case ')':
		p.indents = p.indents[:len(p.indents)-1] // pop
	case ' ':
		if t.size > p.width {
			p.width = p.indents[len(p.indents)-1] - 1
			fmt.Fprintf(&p.Buffer, "\n%*s", margin-p.width, "")
		} else {
			p.WriteByte(' ')
			p.width--
		}
	}
}
func (p *printer) stringf(format string, args ...interface{}) {
	p.string(fmt.Sprintf(format, args...))
}

func pretty(p *printer, v reflect.Value) error {
	switch v.Kind() {
	case reflect.Invalid:
		p.string("nil")

	case reflect.Int, reflect.Int8, reflect.Int16,
		reflect.Int32, reflect.Int64:
		p.stringf("%d", v.Int())

	case reflect.Uint, reflect.Uint8, reflect.Uint16,
		reflect.Uint32, reflect.Uint64, reflect.Uintptr:
		p.stringf("%d", v.Uint())

	case reflect.String:
		p.stringf("%q", v.String())

	case reflect.Array, reflect.Slice: // (value ...)
		p.begin()
		for i := 0; i < v.Len(); i++ {
			if i > 0 {
				p.space()
			}
			if err := pretty(p, v.Index(i)); err != nil {
				return err
			}
		}
		p.end()

	case reflect.Struct: // ((name value ...)
		p.begin()
		for i := 0; i < v.NumField(); i++ {
			if i > 0 {
				p.space()
			}
			p.begin()
			p.string(v.Type().Field(i).Name)
			p.space()
			if err := pretty(p, v.Field(i)); err != nil {
				return err
			}
			p.end()
		}
		p.end()

	case reflect.Map: // ((key value ...)
		p.begin()
		for i, key := range v.MapKeys() {
			if i > 0 {
				p.space()
			}
			p.begin()
			if err := pretty(p, key); err != nil {
				return err
			}
			p.space()
			if err := pretty(p, v.MapIndex(key)); err != nil {
				return err
			}
			p.end()
		}
		p.end()

	case reflect.Ptr:
		return pretty(p, v.Elem())

	default: // float, complex, bool, chan, func, interface
		return fmt.Errorf("unsupported type: %s", v.Type())
	}
	return nil
}

6.访问结构体字段标签

用结构体字段标签来修改 Go 结构值的 JSON 编码方式

下面的 Unpack 函数做了三件事情,首先,调用 req.ParseForm()来解析请求,在这之后 req.Form 就有了所有的请求参数,这个方法对 HTTP GET 和 POST 请求都适用.接着 Unpack 函数构造了一个从每个有效字段名到对应字段变量的映射,在字段有标签时有效字段名与实际字段名可能会有差别,reflect.Type 的 Field 方法会返回一个 reflect.StructField 类型,这个类型提供了每个字段的名称,类型以及一个可选的标签,它的 Tag 字段类型为 reflect.StructTag,底层类型为字符串,提供了一个 Get 方法用于解析和提供对于一个特定健的子串.

// Unpack populates the fields of the struct pointed to by ptr
// from the HTTP request parameters in req.
func Unpack(req *http.Request, ptr interface{}) error {
	if err := req.ParseForm(); err != nil {
		return err
	}

	// Build map of fields keyed by effective name.
	fields := make(map[string]reflect.Value)
	v := reflect.ValueOf(ptr).Elem() // the struct variable
	for i := 0; i < v.NumField(); i++ {
		fieldInfo := v.Type().Field(i) // a reflect.StructField
		tag := fieldInfo.Tag           // a reflect.StructTag
		name := tag.Get("http")
		if name == "" {
			name = strings.ToLower(fieldInfo.Name)
		}
		fields[name] = v.Field(i)
	}

	// Update struct field for each parameter in the request.
	for name, values := range req.Form {
		f := fields[name]
		if !f.IsValid() {
			continue // ignore unrecognized HTTP parameters
		}
		for _, value := range values {
			if f.Kind() == reflect.Slice {
				elem := reflect.New(f.Type().Elem()).Elem()
				if err := populate(elem, value); err != nil {
					return fmt.Errorf("%s: %v", name, err)
				}
				f.Set(reflect.Append(f, elem))
			} else {
				if err := populate(f, value); err != nil {
					return fmt.Errorf("%s: %v", name, err)
				}
			}
		}
	}
	return nil
}

//!-Unpack

//!+populate
func populate(v reflect.Value, value string) error {
	switch v.Kind() {
	case reflect.String:
		v.SetString(value)

	case reflect.Int:
		i, err := strconv.ParseInt(value, 10, 64)
		if err != nil {
			return err
		}
		v.SetInt(i)

	case reflect.Bool:
		b, err := strconv.ParseBool(value)
		if err != nil {
			return err
		}
		v.SetBool(b)

	default:
		return fmt.Errorf("unsupported kind %s", v.Type())
	}
	return nil
}

//!-populate

7.显示类型的方法

reflect.Type 显示任意一个值的类型并枚举它的方法

reflect,Type 和 reflect.Value 都有一个叫做 Method 的方法,每个 t.Method(i)从 reflect.TYpe 调用都会返回一个 reflect.Method 类型的实例,这个结构类型描述了这个方法的名称和类型,而每个 v.Method(i)从 reflect.Value 调用都会返回一个 reflect.Value,代表一个方法值,既一个已绑定接受者的方法,使用 reflect.Value.Call 方法可以调用 Func 类型的 Value

//!+print
// Print prints the method set of the value x.
func Print(x interface{}) {
	v := reflect.ValueOf(x)
	t := v.Type()
	fmt.Printf("type %s\n", t)

	for i := 0; i < v.NumMethod(); i++ {
		methType := v.Method(i).Type()
		fmt.Printf("func (%s) %s%s\n", t, t.Method(i).Name,
			strings.TrimPrefix(methType.String(), "func"))
	}
}

8.反射注意事项

1.基于反射的代码都是很脆弱的

2.类型其实也算是某种形式的文档,而反射的相关操作则无法做静态类型检查,所以大量使用反射的代码是很难理解的

3.基于反射的函数会比特定类型优化的函数慢一两个数量级

9.类型断言

  • 类型断言(一)
package main

import "fmt"

type people struct {
	Name string
}

func main(){
	var i interface{} = &people{"墨持"}

	//类型断言
	if v,ok := i.(*people);ok{
		fmt.Print(v.Name)
	}else{
		fmt.Println("类型匹配失败")
	}

}
C:\Users\vSphere\go\src\day04>go run 05.go
墨持
  • 类型断言(二)
package main

import (
	"fmt"
)

type people struct {
	Name string
}

func main(){
	var i interface{} = people{"乘志"}

	switch i.(type) {
	case people:
		fmt.Println("people")
	case *people:
		fmt.Println("*people")
	default:
		fmt.Println("类型匹配失败")

	}
}
C:\Users\vSphere\go\src\day04>go run 05.go
people
  • DevOps

    DevOps(Development 和 Operations 的组合词)是一组过程、方法与系统的统称,用于促进开发(应用程序/软件工程)、技术运营和质量保障(QA)部门之间的沟通、协作与整合。

    31 引用 • 23 回帖 • 3 关注

广告 我要投放

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...