• Golang 结构体(struct)


    前言

    编程就是要通过编程语言表达给计算机,让计算机帮助我们达到解决现实生活问题的目的!

    不管是Python还是Golang...这些编程语言,由于历史原因、遇到的痛点、解决的问题不同,导致语法追求、本身特性不同。但是遇到的问题、解决问题的思想是一致的。

    面向对象编程 :就是按照自己的理解 尽量把程序里出现的所有东西   抽象得划分为1个个的不同的分类,这些分类中包含自身独有的数据、也有自己独特的方法!

    如果想要开发1款游戏,游戏中的人物不仅有角色属性、也有交易、攻击这些作为。

    单纯得使用数据类型int、string ..函数去表示1个人物,复杂不利于代码灵活、扩展,于是想办法如何把数据和方法集合到1块进行表示。

    Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。

    Go语言中通过结构体的内嵌再配合接口  比面向对象具有更高的扩展性和灵活性

     结构体:就是可以把多种不同的基本数据类型,封装到1个整体里面。

    自定义类型

    自定义类型可以对Go中现有的数据类型的方法进行扩展

    类型别名

    类型别名还记得用于表示英文字符、中文字符的 byte和rune 是uint8和int32类型的类型别名吗?
    为什么会有类型别名?
    我们使用int类型声明1个字符变量看起来不贴切于人类的思维,为了让代码看起来更加清晰易懂,Go语言的作者们使用类型别名
    rune和bute来表示字符。
    //自定义类型和类型别名
    
    //在Go语言里使用 type声明类型
    
    //type 我
    package main
    import "fmt"
    
    
    type myInt int //自定义类型
    type yourInt =int //类型别名
    /*类型别名:还记得用于表示英文字符和中文字符的 byte和rune是int的类型别名吗?
    就是这个意思yourInt本质上还是int,二者视为同1个类型
    为什么会有类型别名?
    我们使用int类型声明1个字符变量看起来不贴切于人类的思维,为了代码清晰使用类型别名
    rune和bute来表示字符
    
    */
    func main()  {
    	var n myInt
    	n=100
    	var m yourInt
    	m=100
    	var c1 byte //byte是由uint8实现,所以byte是uint8的别名,在Go中使用数字表示字符。二者本质还是uint8
    	c1='H'
    	var c2 uint8
    	c2='i'
    	var c3 rune//rune是由int32实现,所以rune是int32的别名,在Go中使用数字表示字符。二者本质还是int32
    	c3='根'
    	var c4 int32 
    	c4='哥'
    
    	
    
    	fmt.Printf("%T
    ",n)//main.myInt
    	fmt.Printf("%T
    ",m)//int
    	fmt.Printf("%T
    ",c1)//uint8
    	fmt.Printf("%T
    ",c2)//uint8
    	fmt.Printf("%T
    ",c3)//int32
    	fmt.Printf("%T
    ",c4)//int32
    
    }
    

      

    结构体声明

    structure别看英文了,结构:顾名思义肯定是由不同的东西组合而成。

    在我们写代码的时候如果 需要定义1个由多个基本数据类型组成的数据类型时(例如人有性别string、年龄uint8、爱好得有多个[]string、吃、喝、拉、撒、睡、学习、工作func)创造这种具有多维度的属性时的物时,无法使用单一的数据类型表示全面,所有我们只能使用结构体。

    结构体的语法

    使用typestruct关键字来定义结构体,具体代码格式如下:

    type 类型名 struct {
        字段名 字段类型
        字段名 字段类型
        …
    }
    

    定义1个简单的结构体

    package main
    
    import "fmt"
    
    //定义1个person类的结构体
    type person struct{
    	name string
    	age uint8
    	married bool
    	hobbies []string
    	education map [string]string
    }
    
    func main() {
    	//声明1个person类型的变量P
    	var p person
    	//给 p赋值
    	p.name="Abe"
    	p.age=64
    	p.married=true
    	p.hobbies=[]string{"爱好广泛","涉猎课本以外的世界"}
    	fmt.Println(p)
    	fmt.Printf("%T
    ",p)//属于main.person类
    	//访问变量p的字段
    	fmt.Println(p.hobbies)
    	
    }		
    

      

    匿名结构体

    在Go语言中如果我们一次性使用结构体的话, 还可以定义 匿名的结构体。

    package main
    import "fmt"
    
    func main(){
    	//定义1个匿名结构体:多应用于临时场景,不使用多次
    	var Martin struct{
    		married bool
    		age uint8
    	}
    	Martin.married=false
    	Martin.age=18
    	fmt.Printf(" type:%T
    details:%#v
    ",Martin,Martin)
    
    }
    

    方式2

    func main() {
    	var struct1 = struct {
    		name string
    		age  uint8
    	}{"Tony", 20}
    	fmt.Println(struct1.name)
    }
    

      

    创建指针类型结构体

    Go中的结构体不同于Python的class,它是值类型不可以被修改要修改可以用指针,所以Python里面没有指针,使用copy、 deep copy。

    package main
    
    import "fmt"
    
    type person struct{
    	name string
    	age uint8
    }
    
    //修改结构体:在Go语言里面参数永远都是副本,无法修改变量p
    func modify(p person){
    	p.age=19
    }
    
    
    func main(){
    
    var p person
    p.name="Martin"
    p.age=18
    //开始修改变量p
    modify(p)
    //不会影响变量p
    fmt.Println(p.age)//18
    
    }
    

      

    通过指针创建1个可以字段可以被修改的结构体

    package main
    
    import "fmt"
    
    type person struct {
    	name string
    	age  uint8
    }
    
    func modifyReally(p *person) {
    	// (*p).age=17//通过指针拿到变量值进行修改
    	p.age = 17 //Go支持的快捷方式:自动根据指针找到变量
    }
    
    func main() {
    	var p person
    	p.name = "Martin"
    	p.age = 18
    	fmt.Println(p.age) //18
    	//传入1个指针进行修改
    	modifyReally(&p)
    	fmt.Println(p.age) //17
    
    }
    

      

    结构体初始化

    Go中结构体初始化的方式很多主要有变量赋值、构造函数初始化2种方式,于Python不同的是go中没有自带构造方法,需要自己构建。

    //结构体初始化的方式
    package main
    
    import "fmt"
    
    //1.声明1个结构体
    type person struct {
    	name string
    	age  uint8
    }
    
    //2.返回值类型struct的构造函数
    func newPersonValue(name string, age uint8) person {
    	//别人调用我,我retrun一个person类型的变量(值类型)
    	return person{
    		name: name,
    		age:  age,
    	}
    }
    
    //3.返回指针类型struct的构造函数
    func newPerson(name string, age uint8) *person {
    	//别人调用我,我retrun一个person类型的变量的指针
    	return &person{
    		name: name,
    		age:  age,
    	}
    }
    
    //4.初始化struct
    func main() {
    	//方法1:通过变量声明然后赋值初始化
    	var p person
    	p.name = "Tom"
    	p.age = 19
    	//方法2:键值对初始化
    	var p2 = person{
    		name: "Jack",
    		age:  20,
    	}
    	//方法3:值列表初始化
    	var p3 = person{
    		"Werwilson",
    		23,
    	}
    	//方法4:构造函数初始化(指针类型)
    	p4 := newPerson("Jessica", 28)
    
    	//方法5:构造函数初始化(值类型)
    	p5 := newPersonValue("Derrick", 38)
    	fmt.Println(p.name)
    	fmt.Println(p2.name)
    	fmt.Println(p3.name)
    	fmt.Println(p4.name)
    	fmt.Println(p5.name)
    
    }
    

      

    变量赋值初始化结构体

    package main
    
    import "fmt"
    
    type person struct {
    	name string
    	age  uint8
    }
    
    func main() {
    	//1.先声明变量类型然后再进行初始化初始化结构体
    	var p1 person
    	p1.name = "Martin"
    	p1.age = 18
    	fmt.Println(p1.age) //18
    	//2.new开辟内存返回指针初始化结构体
    	var p2 = new(person)
    	(*p2).name = "Martin"
    	p2.name = "张根" //等同于(*p1).name="Martin"
    	p2.age = 19
    	fmt.Println(p2.name)
    	fmt.Println(p2.age)
    	//3.声明变量类型同时初始化结构体(key value版)
    	var p3 = &person{name: "Jack", age: 28}
    	fmt.Println(p3.name)
    	fmt.Println(p3.age)
    	//
    	p4 := &person{
    		"Rose",
    		23,//注意最后的item也要有逗号!!值顺序和结构体定义的字段顺序一致,
    	}
    	fmt.Println(p4.name)
    	fmt.Println(p4.age)
    
    }
    

    构造函数初始化结构体

    构造函数 就是1个构造X种结构体变量的函数,其用意是通过 1个函数反复生成某种结构体的变量,提升代码的重用性。

    使用变量初始化结构体的方式会造成代码的冗余,我么可以使用一个构造函数来完成struct的初始化

    struct的构造函数约定俗成以 new开头,自定义1个构造函数可以返回1个值类型的struct, 如果1个struct内部字段存储的数据量很大,重复copy造成内存开销过大。也可以返回1个指针类型的struct。

    package main
    
    import "fmt"
    
    type person struct {
    	name string
    	age  uint8
    }
    
    //自定义1个构造函数:返回1个结构体类型
    func newPereson(name string, age uint8) person {
    	//在构造函数中完成 struct 的初始化过程
    	return person{
    		name: name,
    		age:  age,
    	}
    
    }
    
    //自定义1个构造函数:返回1个指针 
    //避免struct 内部数据量大的时候,重复copy造成内存开销过大
    //struct的构造函数约定俗成以 new开头
    func newPeresonPointer(name string, age uint8) *person {
    	//在构造函数中完成 struct 的初始化过程
    	return &person{
    		name: name,
    		age:  age,
    	}
    
    }
    
    func main() {
    	//构造函数可以方便、快捷的构造出不同的struct
    	p1 := newPereson("张三", 18)
    	p2 := newPereson("李四", 29)
    	fmt.Println(p1)
    	fmt.Println(p2)
    	//p3是可以修改的,因为initPeresonPointer构造函数返回的是指针类型
    	p3 := newPeresonPointer("张根", 27)
    	p3.age = 28 //(*p3).age=28
    	fmt.Println(p3.age)
    
    }
    

      

    结构体模拟面向对象继承效果

    面向对象中的继承可以,重用代码,避免重复造轮子,那么怎么使用Go的struct模拟继承的效果呢?

    匿名字段struct

    匿名字段就是没有字段名称,由于使用数据类型取值,它适用于 struct字段较少的场景。
    package main
    import "fmt"
    //匿名字段:匿名字段就是没有字段名称
    //匿名字段适用于 struct字段较少的场景
    type person struct{
    	string 
    	int32
    }
    
    func main(){
    	p1:=person{"Martin",20}
    	//如果没有字段名称通过什么取值呢?数据类型!
        fmt.Println(p1.string)
    	fmt.Println(p1.int32)
    
    }
    

    嵌套struct

    为了实现更深层的数据封装,结构体里也可以套结构体。

    //嵌套结构体
    package main
    
    import "fmt"
    
    //员工个公司共有的地址属性
    type address struct {
    	province string
    	city     string
    }
    
    //员工信息struct
    type employee struct {
    	name string
    	age  int8
    	addr address //嵌套了结构体address
    }
    
    //公司信息struct
    
    type company struct {
    	name string
    	addr address //嵌套了结构体address
    }
    
    func main() {
    	employee1 := employee{
    		name: "Robinz",
    		age:  29,
    		addr: address{
    			province: "山西省",
    			city:     "阳泉市",
    		},
    	}
    	company1 := company{
    		name: "baix",
    		addr: address{province: "北京市", city: "海淀区"},
    	}
    	fmt.Println(employee1.name)
    	fmt.Println(employee1.addr.province)
    	fmt.Println(employee1.addr.city)
    	fmt.Println(company1.addr.city)
    }
    

      

    匿名嵌套struct 

    上面我们使用了嵌套结构体,在使用struct取值时需要 employee1.addr.city、employee1.addr.province点2次,如何不点2次呢?

    匿名嵌套结构体: 先在自己的struct里面查找字段,如果查找不到该字段,再去匿名嵌套的struct查找。
    注意如果:1个struct中定义了2个匿名字段的struct(且2个匿名struct的字段名1样,就无法查找。)
    //匿名嵌套结构体
    package main
    
    import "fmt"
    
    //员工个公司共有的地址属性
    type address struct {
    	province string
    	city     string
    }
    
    //员工信息struct
    type employee struct {
    	name    string
    	age     int8
    	address //嵌套匿名字段的结构体address
    }
    
    //公司信息struct
    
    type company struct {
    	name    string
    	address //嵌套了匿名字段的结构体address
    }
    
    func main() {
    	employee1 := employee{
    		name: "Robinz",
    		age:  29,
    		address:address{
    			province: "山西省",
    			city:     "阳泉市",
    		},
    	}
    	company1 := company{
    		name: "baix",
    		address:address{province: "北京市", city: "海淀区"},
    	}
    	fmt.Println(employee1.name)
    	fmt.Println(employee1.province)
    	fmt.Println(employee1.city)//先在自己的struct里面查询字段 再去匿名嵌套的struct查询
    	fmt.Println(company1.city)//先在自己的struct里面查询字段 再去匿名嵌套的struct查询
    }
    

      

     如果1个struct嵌套了2个字段相同的匿名struct,那么我该去这2个匿名struct中哪一个里面取查找呢?所以会引发冲突

    //匿名嵌套结构体
    package main
    
    import "fmt"
    
    //员工个公司共有的地址属性
    type address struct {
    	province string
    	city     string
    }
    
    //工作地址:和 adress中的 province好city字段出现了冲突
    type workAdreess struct {
    	province string
    	city     string
    }
    
    //员工信息struct
    type employee struct {
    	name    string
    	age     int8
    	address //嵌套匿名字段的结构体address  字段冲突
    	workAdreess////嵌套匿名字段的结构体workAdreess字段冲突
    }
    
    //公司信息struct
    type company struct {
    	name    string
    	address //嵌套了匿名字段的结构体address
    }
    
    func main() {
    	employee1 := employee{
    		name: "Robinz",
    		age:  29,
    		address: address{
    			province: "山西省",
    			city:     "阳泉市",
    		},
    		workAdreess: workAdreess{province: "山东省", city: "威海"},
    	}
    	company1 := company{
    		name:    "baix",
    		address: address{province: "北京市", city: "海淀区"},
    	}
    	fmt.Println(employee1.name)
    	fmt.Println(employee1.name)
    	// fmt.Println(employee1.city)//先在自己的struct里面查询字段 再去匿名嵌套的struct查询
    	fmt.Println(company1.city) //先在自己的struct里面查询字段 再去匿名嵌套的struct查询
    
    	//如果employee结构体中嵌套了2个含有相同字段的匿名结构体,会引起查询冲突,只能按照以下方式取值
    	fmt.Println(employee1.address.city)
    	fmt.Println(employee1.workAdreess.province)
    }
    

      

    模拟继承

    利用Go的struct可以嵌套struct,当前struct中没有的字段    自动去嵌套了struct的匿名字段中查找的特性,实现继承的效果。

    package main
    
    import "fmt"
    
    //基类
    type animal struct {
    	kind   string
    	gender string
    	age    uint8
    }
    
    //子类(人类)
    type perosn struct {
    	animal
    }
    
    //子类(犬类)
    type dog struct {
    	animal
    }
    
    //给基类增加walk方法
    func (a animal) walk() {
    	fmt.Printf("%s are walking.. 
    ", a.kind)
    }
    
    func main() {
    	p1 := perosn{
    		animal: animal{kind: "People", age: 18, gender: "男性"},
    	}
    	//p1 struct里面没有walk方法,就自动去匿名字段animal这个匿名结构体中查找
    	p1.walk()
    	d1 := dog{
    		animal: animal{kind: "Dogs", age: 3, gender: "雄性"},
    	}
    	d1.walk()
    	//d1 struct里面没有walk方法,也自动去匿名字段animal这个匿名结构体中查找
    }
    

      

      

    结构体内存布局

    结构体占用一块连续的内存。

    package main
    
    import "fmt"
    
    //结构体占用1块连续的内存
    type x struct {
    	a int8 //8位=1个字节
    	b int8
    	c int8
    }
    
    func main() {
    	m := x{
    		1,
    		2,
    		3,
    	}
    	fmt.Printf("字段a的内存地址:%p
    ", &m.a)
    	fmt.Printf("字段b的内存地址:%p
    ", &m.b)
    	fmt.Printf("字段c的内存地址:%p
    ", &m.c)
    
    }
    /*
    字段a的内存地址:0xc00004a080
    字段b的内存地址:0xc00004a081
    字段c的内存地址:0xc00004a082
    */
    

      

    struct的方法和接受者

    方法就是在 函数名前指定了接收者函数指定接收者就是指定了哪1种数据类型的变量,才可以调用这个函数

    前面的struct中我只是封装了数据,那么我想对struct中的这些数据进行操作呢?就需要给struct绑定上1个方法。

    Go语言中的方法(Method)是一种作用于特定类型变量的函数

    这种特定类型变量叫做接收者(Receiver)

    接收者的概念就类似于其他语言中的this或者 self

    Go里面接收者(某1个数据类型)+方法 这套语法,实现了类似于Python类中的方法!

    语法

    原来Go的函数名前面还可以指定接收者

    func (接收者变量 接收者类型) 方法名(参数列表) (返回参数) {
        函数体
    }
    

    值类型的接受者

    接受者必须指定某1个数据类型

    package main
    import "fmt"
    
    
    type dog struct{
    	kind string
    	age uint8
    }
    
    //方法和接收者
    func newDog(kind string,age uint8)(*dog){
    	return &dog{
    		kind:kind,
    		age:age,
    	}
    
    }
    
    
    //函数名前面可以指定这个函数的接受者,如果该函数指定了接收这,这个函数就叫method方法
    //因为指定了接收者,所以方法是作用于特定类型的函数
    //接受者使用类型的首字母 小写表示
    // dog 类型是接收者 bark就是仅作用于dog类的方法
    func (d dog)bark(){
    	fmt.Printf("A %s barks at you~
    ",d.kind)
    }
    
    func main(){
    
    	d1:=newDog("中华田园犬",2)
    	//因为接受者和方法做了绑定,所以dog类的对象都可以调用方法 bark 方法
    	d1.bark()
    
    
    }
    

      

     指针类型的接受者

     接受者还可以为 某种数据类型的指针,接受者为数据类型指针时就实现了对struct 字段的修改。

    package main
    
    import "fmt"
    
    //定义1个struct person
    type person struct {
    	name string
    	age uint8
    }
    //定义1个用于初始 person结构体的构造函数 
    func newPerson(name string, age uint8) *person {
    	return &person{
    		name:name,
    		age:age,
    	}
    
    }
    
    //使用指针接受者:接收者不仅可以为自定义的数据类型,也可以是数据类型的指针类型
    func (p *person)aged(years uint8){
    	p.age+=years
    }
    
    
    
    func main() {
    	p1:=newPerson("Someone you don't like",39)
    	fmt.Println(p1.age)
    	p1.aged(10)
    	fmt.Println(p1.age)
    	p1.aged(10)
    	fmt.Println(p1.age)
    	p1.aged(10)
    	fmt.Println(p1.age)
    }
    

    任意类型添加方法 

    在Go语言中,接收者的类型可以是任何类型不仅仅是结构体,任何类型都可以拥有方法。 举个例子,我们基于内置的int32类型使用type关键字可以定义新的自定义类型,然后为我们的自定义类型添加方法。

    package main
    
    import "fmt"
    //给go内置的数据类型扩展方法
    type myInt int64
    
    //给int32扩展1个翻x倍的方法
    func(m *myInt)autoTimes(n int64 ){
    	(*m)*=myInt(n)
    }
    
    func main(){
    	var salary myInt
    	salary=2500
    	//娶媳妇的年级了,给自己涨点工资吧....
    	salary.autoTimes(1000000)
    	fmt.Println(salary)
    	
    }
    

    结构体序列化

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。JSON键值对是用来保存JS对象的一种方式,键/值对组合中的键名写在前面并用双引号""包裹,使用冒号:分隔,然后紧接着值;多个键值之间使用英文,分隔。

    序列化和反序列化

    //1.序列化:把Go语言中的结构体变量------json格式的字符串
    //2.反序列化:把json格式的字符串---------Go语言能识别的结构体变量
    /*
    由于我们使用了第三方的包,而main包中声明的变量
    无法在第三包中使用(除非大写才能被main包之外的包使用)
    所以想要访问main中的变量必须大写!
    
    如果必须大写。我们产生的json数据也会变成大写,为了避免数据失真,可以使用tag
    */
    type person struct {
    	Name string `json:"name" db:"name" ini:"name"`
    	Age  uint8  `json:"age" db:"name" ini:"name"`
    }
    
    func main() {
    
    	p1 := person{
    		Name: "Martin",
    		Age:  18,
    	}
    	//序列化
    	b, err := json.Marshal(p1)
    	if err != nil {
    		fmt.Printf("marshal faild err:%v", err)
    		return
    	}
    	fmt.Printf("%#v
    ", string(b)) //字符串本身是由字节切片组成的,所有支持强制转换。
    
    	//反序列化
    	var p2 person
    	json.Unmarshal([]byte(string(b)), &p2)
    	fmt.Printf("%#v
    ", p2)
    
    }
    

    复杂json结构

    package main
    
    import (
    	"encoding/json"
    	"fmt"
    )
    
    type Person struct{
    	Name string
    	Age int
    }
    
    var group1 []*Person
    
    func main(){
    	group1=[]*Person{
    		&Person{
    			"Martion",
    			18,
    		},
    		&Person{
    			"Toney",
    			38,
    		},
    	}
    	b,err:=json.Marshal(group1)
    	if err!=nil{
    		fmt.Println("序列化失败",err)
    	}
    	//fmt.Println(b,err)
    	err=json.Unmarshal(b,&group1)
    	if err!=nil{
    		fmt.Println("反序列化失败",err)
    	}
    	fmt.Printf("%#v
    ", group1[0].Name)
    
    	}
    

      

    结构体实现链表

    链表反转

    // package main
    
    // import "fmt"
    
    // type person struct {
    // 	name string
    // 	age  uint8
    // }
    
    // //自定义1个构造函数:返回1个结构体类型
    // func newPereson(name string, age uint8) person {
    // 	//在构造函数中完成 struct 的初始化过程
    // 	return person{
    // 		name: name,
    // 		age:  age,
    // 	}
    
    // }
    
    // //自定义1个构造函数:返回1个指针
    // //避免struct 内部数据量大的时候,重复copy造成内存开销过大
    // //struct的构造函数约定俗成以 new开头
    // func newPeresonPointer(name string, age uint8) *person {
    // 	//在构造函数中完成 struct 的初始化过程
    // 	return &person{
    // 		name: name,
    // 		age:  age,
    // 	}
    
    // }
    
    // func main() {
    // 	//构造函数可以方便、快捷的构造出不同的struct
    // 	p1 := newPereson("张三", 18)
    // 	p2 := newPereson("李四", 29)
    // 	fmt.Println(p1)
    // 	fmt.Println(p2)
    // 	//p3是可以修改的,因为initPeresonPointer构造函数返回的是指针类型
    // 	p3 := newPeresonPointer("张根", 27)
    // 	p3.age = 28 //(*p3).age=28
    // 	fmt.Println(p3.age)
    
    // }
    
    package main
    
    import "fmt"
    
    //链表就是:每1个节点都会记录本节点next节点的指针(内存地址)
    type linkedList struct {
    	value int
    	//在结构体里面引用结构体自己也是可以的
    	next *linkedList
    }
    
    var linkedList1 = &linkedList{
    	value: 1,
    	next: &linkedList{
    		value: 2,
    		next: &linkedList{
    			value: 3,
    			next: &linkedList{
    				value: 4,
    				next: &linkedList{
    					value: 5,
    					next:  nil,
    				},
    			},
    		},
    	},
    }
    
    func reverseList(head *linkedList) *linkedList {
    	//关键点:新的反转链表第1个值个应该为nil,才可以站在当前位置 设置old 链表的下1值,的前1个为自己!
    	var newPreviousOne *linkedList
    	//当前结构体
    	current := head
    	//当前结构体的值为空时说明遍历链表到了尽头
    	for current != nil {
    		//把旧链表中的下1个节点的值缓存起来
    		oldNextOne := current.next
    		//开始反转新的链表
    		current.next = newPreviousOne
    		//1步步地向前反转
    		newPreviousOne= current
    		//保持for循环1步步地在old链表里向前进行
    		current=oldNextOne
    	}
    	return newPreviousOne
    }
    
    func main() {
    	ret := reverseList(linkedList1)
    	for ret != nil {
    		fmt.Println(ret.value, "->")
    		ret = ret.next
    	}
    }
    

      

      

    练习 

    员工管理函数版

    /*
    函数版学生管理系统:
    该系统能查看/新增/删除学生使用函数实现
    */
    
    package main
    
    import (
    	"fmt"
    	"os"
    )
    
    //变量声明
    var (
    	database    map[int]student
    	studentID   int
    	studentName string
    	studentAge  uint8
    	oprate      uint8
    	exitSignal  byte
    )
    
    type student struct {
    	name string
    	age  uint8
    }
    
    //学生结构体
    func newStuden(name string, age uint8) student {
    	return student{
    		name: studentName,
    		age:  studentAge,
    	}
    }
    
    //增加学生
    func addStudent() {
    	fmt.Print("请输入学生id:")
    	fmt.Scan(&studentID)
    	fmt.Print("请输入姓名:")
    	fmt.Scan(&studentName)
    	fmt.Print("请输入年龄:")
    	fmt.Scan(&studentAge)
    	database[studentID] = newStuden(studentName, studentAge)
    }
    
    //查看数据库
    func showStudents() {
    	for id, s := range database {
    		fmt.Printf("学生ID:%d 姓名:%s 年龄:%d 
    ", id, s.name, s.age)
    	}
    }
    
    //根据主键删除
    func deleteStudent() {
    	fmt.Print("请输入学生id:")
    	fmt.Scan(&studentID)
    	delete(database, studentID)
    }
    
    //for {}死循环显示程序菜单
    func showOperation() {
    	for {
    		fmt.Println(`
    		1.新增学生
    		2.删除学生
    		3.查看学生
    		4.退出
    					 `)
    		fmt.Printf("请输出操作:")
    		fmt.Scan(&oprate)
    		switch oprate {
    		case 1:
    			addStudent()
    		case 2:
    			deleteStudent()
    		case 3:
    			showStudents()
    		case 4:
    			os.Exit(1)
    		}
    
    	}
    
    }
    
    func main() {
    	//初始化数据库
    	database = make(map[int]student, 20)
    	//死循环:程序菜单
    	showOperation()
    
    }
    

      

    员工管理面向对象版

     

     manage_unit.go

    package manage
    import (
    	"fmt"
    	"sort"
    )
    
    var (
    	id   int
    	name string
    	age  uint8
    )
    
    //Employee 雇员
    type Employee struct {
    	name string
    	age  uint8
    }
    
    //newEmployee 构造函数
    func newEmployee(name string, age uint8) Employee {
    	return Employee{
    		name: name,
    		age:  age,
    	}
    }
    //修改 employee信息
    func(E *Employee)modifyEmployee(name string,age uint8 ){
    	E.name=name
    	E.age=age
    }
    
    
    //Employer  雇主
    type Employer struct {
    	db map[int]Employee
    }
    
    //NewEmployer 构造函数
    func NewEmployer(count int) Employer {
    	return Employer{
    		db: make(map[int]Employee, count),
    	}
    }
    
    //Add 方法
    func (E *Employer) Add() {
    	fmt.Print(`请输入员工ID: `)
    	fmt.Scan(&id)
    	fmt.Print(`请输入员工姓名:`)
    	fmt.Scan(&name)
    	fmt.Print(`请输入员工年龄:`)
    	fmt.Scan(&age)
    	E.db[id] = newEmployee(name,age)
    
    }
    
    
    
    //Del 删除员工信息
    func (E *Employer) Del() {
    	fmt.Print(`请输入员工ID: `)
    	fmt.Scan(&id)
    	delete(E.db, id)
    
    }
    
    //Update 更新员工信息
    func (E *Employer) Update() {
    	fmt.Print(`请输入员工ID: `)
    	fmt.Scan(&id)
    	fmt.Print(`请输入员工姓名:`)
    	fmt.Scan(&name)
    	fmt.Print(`请输入员工年龄:`)
    	fmt.Scan(&age)
    	worker:=E.db[id]
    	fmt.Printf("原来work内存地址:%p
    ",&worker)
    	worker.modifyEmployee(name,age)
    	fmt.Printf("现worker内存地址%p
    ",&worker)
    	E.db[id]=worker
    
    }
    
    
    //ShowAll 方法
    func (E *Employer) ShowAll() {
    	fmt.Println("开始查看")
    	if len(E.db) == 0 {
    		fmt.Println("暂无员工数据")
    		return
    	}
    	//按id大小顺序 显示员工信息
    	var keys=make([]int, 0,len(E.db))
    	for key:= range E.db{
    		keys = append(keys,key)
    		
    	}
    	sort.Ints(keys)
    	for _, key := range keys {
    		worker:=E.db[key]
    		fmt.Printf("员工ID:%d 员工姓名:%s 员工年龄:%d
    ", key, worker.name, worker.age)
    	}
    
    }
    

    main.go

    package main
    import (
    	"fmt"
    	//Go导入包从环境变量配置的go project/src文件夹下开始
    	//tools文件夹下.go文件中 package的manage
    	manage "hello/crm/tools"
    	"os"
    )
    
    var (
    	operate uint8
    )
    
    func screen() {
    	//初始化1个雇主的对象(有自己的数据库,拥有对数据库正删改查的权限)
    	employerObj := manage.NewEmployer(200)
    	for {
    		fmt.Println(`
    		1.添加员工
    		2.删除员工
    		3.修改员工
    		4.查看所有
    		5.退出
    		`)
    		fmt.Print(">>")
    		fmt.Scan(&operate)
    		switch operate {
    		case 1:
    			employerObj.Add()
    		case 2:
    			employerObj.Del()
    		case 3:
    			employerObj.Update()
    		case 4:
    			employerObj.ShowAll()
    		case 5:
    			os.Exit(1)
    		default:
    			fmt.Println("输入无效")
    
    		}
    	}
    
    }
    func main() {
    	screen()
    }
    

      

    参考

     
  • 相关阅读:
    C# UrlDecode将+替换为空格问题
    Hashtable无序,用Dictionary代替
    Oracle查找Web执行SQL
    远程连接Oracle服务器
    asp.net core网站SSL nginx配置
    Supervisor踩过的坑
    centos nginx配置支持WebSocket(signalR)
    SignalR在asp.net core下使用
    Hangfire 在asp.net core环境的使用
    liteUploader上传控件的封装使用
  • 原文地址:https://www.cnblogs.com/sss4/p/12651221.html
Copyright © 2020-2023  润新知