乐趣区

关于go:Golang-基础之函数使用-三

大家好,明天将梳理出的 Go 语言函数用法内容,分享给大家。请多多指教,谢谢。

本次《Go 语言函数应用》内容共分为三个章节,本文为第三章节。

  • Golang 根底之函数应用 (一)
  • Golang 根底之函数应用 (二)
  • Golang 根底之函数应用 (三)

本章节内容

  • init 函数
  • 办法

init 函数

介绍

例如某些场景下,咱们须要提前初始化一些变量或逻辑代码。在这种状况下, 咱们能够用一个非凡的 init 初始化函数来简化初始化工作,每个文件都能够蕴含一个或多个 init 初始化函数。

func init() {} // init() 函数语法 

init 初始化函数除了不能被调用或援用外,其余行为和一般函数相似。在每个文件中的 init 初始化函数,在程序开始执行时依照它们申明的程序被主动调用。

init 函数先于 main 函数执行

留神:每个包在解决依赖的前提下,以导入申明的程序初始化,每个包只会被初始化一次。因而,如果一个 p 包导入了 q 包,那么在 p 包初始化的时候能够认为 q 包必然曾经初始化过了。

init 函数的特色

  • init 函数是用于程序执行前做包的初始化的函数,比方初始化包里的变量等
  • init 函数没有输出参数、返回值
  • 每个包能够领有多个 init 函数
  • 包的每个源文件也能够领有多个 init 函数
  • 同一个包中多个 init 函数的执行程序 go 语言没有明确的定义 (阐明)
  • 不同包的 init 函数依照包导入的依赖关系决定该初始化函数的执行程序
  • init 函数不能被其余函数调用,而是在 main 函数执行之前,主动被调用

初始化的过程

  1. 初始化导入的包(程序并不是按导入程序(从上到下)执行的,runtime 须要解析包依赖关系,没有依赖的包最先初始化 );
  2. 初始化包作用域的变量(并非依照“从上到下、从左到右”的程序,runtime 解析变量依赖关系,没有依赖的变量最先初始化 );
  3. 执行包的 init 函数;

runtime 是 go 语言运行所须要的基础设施,也是 go 的外围个性。该内容将放到后续《go 根底之个性》章节为大家分享。

应用

案例:init 初始化程序

package main

import "fmt"

var Num int = Call() // 全局变量申明

func init() { // 初始化函数
    fmt.Println("init()")
}

func Call() int {fmt.Println("Call()")
    return 1
}

func main() {fmt.Println("main()")
}

输入

Call()
init()
main()

论断,初始化的过程:Num 变量初始化 -> init() -> main()

案例:同一个包不同源码的 init 初始化程序

首先创立 3 个文件,main.go 代码中蕴含全局变量、init 初始化函数定义,和 main 函数入口;a.go 和 b.go 代码文件中,只蕴含全局变量、init 初始化函数的定义。

main.go 文件

package main

import ("fmt")

var _ int = m()

func init() {fmt.Println("init in main.go")
}

func m() int {fmt.Println("call m() in main.go")
   return 1
}

func main() {fmt.Println("main()")
}

a.go 文件

package main

import "fmt"

var _ int = a()

func init() {fmt.Println("init in a.go")
}

func a() int {fmt.Println("call a() in a.go")
   return 1
}

b.go 文件

package main

import "fmt"

var _ int = b()

func init() {fmt.Println("init in b.go")
}

func b() int {fmt.Println("call b() in b.go")
   return 1
}

因为 a.go 和 b.go 都归属于 main 包,但没有两文件中没有 main 函数入口。在执行的时候,须要应用 go run main.go a.go b.go 这样模式执行,runtime 会将所有文件进行加载初始化。

输入

call m() in main.go
call a() in a.go
call b() in b.go
init in main.go
init in a.go
init in b.go
main()

论断,同一个包不同源文件的 init 函数执行程序,golang 没做官网阐明。这块加载过程是依照 go run 文件排序。

案例:多个 init 函数初始化程序

package main

import "fmt"

func init() {fmt.Println("init 1")
}

func init() {fmt.Println("init 2")
}

func main() {fmt.Println("main")
}

输入

init 1
init 2
main

论断:init 函数比拟非凡,能够在包里被屡次定义。

办法

介绍

Golang 中办法,实现是以绑定对象实例,并隐式将实例作为第一实参 (receiver)。

定义阐明

  • 只能为以后包内命名类型定义办法;
  • 参数 receiver 可任意命名,如办法中未曾应用,可省略参数名;
  • 参数 receiver 类型能够是 T 或 *T,基类型 T 不能是接口或指针;
  • 不反对办法重载,receiver 只是参数签名的组成部分;
  • 可用实例 valuepointer 调用全副办法,编译器主动转换

一个办法就是一个蕴含了接受者的函数,接受者能够是命名类型或者构造体类型的一个值或者是一个指针。

办法定义

func (recevier type) methodName(参数列表) (返回值列表) {} // 参数和返回值能够省略 

应用

定义一个构造类型和该类型的一个办法

package main

import "fmt"

// 构造体
type Info struct {
    Name  string
    Desc string
}

// 办法
func (u Info) Output() {fmt.Printf("%v: %v \n", u.Name, u.Desc)
}

func main() {
    // 值类型调用办法
    u1 := Info{"帽儿山的枪手", "分享技术文章"}
    u1.Output()
    // 指针类型调用办法
    u2 := Info{"帽儿山的枪手", "分享技术文章"}
    u3 := &u2
    u3.Output()}

输入

 帽儿山的枪手: 分享技术文章 
帽儿山的枪手: 分享技术文章 

匿名办法

如类型 S 蕴含匿名字段 *T,则 S 和 *S 办法集蕴含 T + *T 办法。

这条规定说的是当咱们嵌入一个类型的指针,嵌入类型的接受者为值类型或指针类型的办法将被晋升,能够被内部类型的值或者指针调用。

package main

import "fmt"

type S struct {T}

type T struct {int}

func (t T) testT() {fmt.Println("如类型 S 蕴含匿名类型 *T, 则 S 和 *S 办法集蕴含 T 办法")
}

func (t *T) testP() {fmt.Println("如类型 S 蕴含匿名字段 *T, 则 S 和 *S 办法汇合蕴含 *T 办法")
}

func main() {s1 := S{T{1}}
    s2 := &s1
    fmt.Printf("s1 is : %v\n", s1)
    s1.testT()
    s1.testP() // 晋升指针类型调用

    fmt.Printf("s2 is : %v\n", s2)
    s2.testT() // 晋升值类型调用
    s2.testP()}

输入

s1 is : {{1}}
如类型 S 蕴含匿名类型 *T, 则 S 和 *S 办法集蕴含 T 办法
如类型 S 蕴含匿名字段 *T, 则 S 和 *S 办法汇合蕴含 *T 办法
s2 is : &{{1}}
如类型 S 蕴含匿名类型 *T, 则 S 和 *S 办法集蕴含 T 办法
如类型 S 蕴含匿名字段 *T, 则 S 和 *S 办法汇合蕴含 *T 办法 

表达式

依据调用者不同,办法分为两种表现形式

instance.method(args...) ---> <type>.func(instance, args...)

前者称为 method value,后者 method expression 则须显式传参。

package main

import "fmt"

type User struct {
    id   int
    name string
}

func (self *User) Test() {fmt.Printf("%p, %v\n", self, self)
}

func main() {u := User{1, "帽儿山的枪手"}
    u.Test()

    mValue := u.Test
    mValue() // 隐式传递 receiver

    mExpression := (*User).Test
    mExpression(&u) // 显式传递 receiver
}

输入

0xc00000c018, &{1 帽儿山的枪手}
0xc00000c018, &{1 帽儿山的枪手}
0xc00000c018, &{1 帽儿山的枪手}

论断,办法是指针类型,method value 会复制 receiver。

技术文章继续更新,请大家多多关注呀~~

搜寻微信公众号【帽儿山的枪手】,关注我

退出移动版