• go Print 和 反射



    0. 前言

    小白学标准库之反射 reflect 篇中介绍了反射的三大法则。但并未给出具体示例介绍反射,感觉还是少了点什么。这里进一步通过fmt.Println 源码,查看反射如何使用的,算是对前文的补充。由于文章已经够长了,为方便观看,新开一篇介绍,当然内容不会太多。

    1. fmt.Println 函数

    go 中 Print 系列函数(fmt.Println, fmt.Printf...) 可以打印任意类型,这是怎么做到的呢?结合前面学习我们知道通过反射能够在运行时获取类型值。

    查看 fmt.Println 函数实现:

    func Println(a ...interface{}) (n int, err error) {
    	return Fprintln(os.Stdout, a...)
    }
    
    func Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
    	p := newPrinter()
    	p.doPrintln(a)
    	n, err = w.Write(p.buf)
    	p.free()
    	return
    }
    

    Println 调用 Fprintln 函数,Fprintln 首先 new 一个 Printer p,接着通过 p 执行 doPrintln:

    func (p *pp) doPrintln(a []interface{}) {
    	for argNum, arg := range a {
    		if argNum > 0 {
    			p.buf.writeByte(' ')
    		}
    		p.printArg(arg, 'v')
    	}
    	p.buf.writeByte('\n')
    }
    

    doPrintln 首先解析参数,接着处理参数。重点放在 printArg 这里:

    // Some types can be done without reflection.
    switch f := arg.(type) {
    case bool:
    	p.fmtBool(f, verb)
    case float32:
    	p.fmtFloat(float64(f), 32, verb)
    ...
    
    case reflect.Value:
    	// Handle extractable values with special methods
    	// since printValue does not handle them at depth 0.
    	if f.IsValid() && f.CanInterface() {
    		p.arg = f.Interface()
    		if p.handleMethods(verb) {
    			return
    		}
    	}
    	p.printValue(f, verb, 0)
    default:
    	// If the type is not simple, it might have methods.
    	if !p.handleMethods(verb) {
    		// Need to use reflection, since the type had no
    		// interface methods that could be used for formatting.
    		p.printValue(reflect.ValueOf(f), verb, 0)
    	}
    }
    

    doPrintln 函数内容较多,这里摘出重要部分进行介绍。

    首先,通过类型断言判断接口值,如果判断不出来则走到 default 分支(这也和 小白学标准库之反射 开篇介绍的对应,即类型断言的表示能力有限,更复杂的表达能力需要通过反射),通过反射机制反射接口值。

    如当打印结构体时,分支判断会走到 default,通过反射获取结构体的值:

    func main() {
    	a := person{
    		name: "lubanseven",
    	}
    
    	fmt.Println(a)
    }
    

    这里有几点注意的是:

    1. p.printValue 函数是对反射代码在运行时的处理,相比于直接处理,更加复杂,读写都不容易。这也是在静态语言中使用动态特性付出的成本。关于反射代码在运行时的写法可参考 这里
    2. 不管是 小白学标准库之反射 reflect 还是这篇文章都没有介绍汇编,因为汇编是在编译阶段确定的,而反射的实现是在运行时,通过汇编能看到的是 CALL xxx.Println(SB),无法看到具体运行时的实现。

  • 相关阅读:
    过拟合问题详解
    C++数据结构原理和经典问题求解--绪论
    centos系统 anaconda3(python3)安装pygrib
    pycharm激活教程
    如何查看电脑是几核几线程(网传方法有错误)
    深度学习过程
    VS2010 编译 boost thread库
    windows多线程编程
    matplotlib画条形图
    matplotlib画折线图,并以时间作为横轴
  • 原文地址:https://www.cnblogs.com/xingzheanan/p/16084592.html
Copyright © 2020-2023  润新知