失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > go var type 互转_Go语言学习笔记(第九章) 结构体

go var type 互转_Go语言学习笔记(第九章) 结构体

时间:2020-09-06 01:57:01

相关推荐

go var type 互转_Go语言学习笔记(第九章) 结构体

Go语言基础之结构体

Go语言中没有“类”的概念,也不支持“类”的继承等面向对象的概念。Go 通过类型别名(alias types)和结构体的形式支持用户自定义类型,或者叫定制类型。试图表示一个现实世界中的实体。

类型别名和自定义类型

自定义类型

在Go语言中有一些基本的数据类型,如string整型浮点型布尔等数据类型, Go语言中可以使用type关键字来定义自定义类型。

自定义类型是定义了一个全新的类型。我们可以基于内置的基本类型定义,也可以通过struct定义。例如:

// 将MyInt定义为int类型type MyInt int

通过type关键字的定义,MyInt就是一种新的类型,它具有int的特性。

类型别名

类型别名是Go1.9版本添加的新功能。

类型别名规定:TypeAlias只是Type的别名,本质上TypeAliasType是同一个类型。就像一个孩子小时候有小名、乳名,上学后用学名,英语老师又会给他起英文名,但这些名字都指的是他本人。

type TypeAlias = Type

我们之前见过的runebyte就是类型别名,他们的定义如下:

type byte = uint8type rune = int32

类型定义和类型别名的区别

类型别名与类型定义表面上看只有一个等号的差异,我们通过下面的这段代码来理解它们之间的区别。

// 类型定义type NewInt int// 类型别名type MyInt = intfunc main() {var a NewInt var b MyInt fmt.Printf("type of a:%T\n", a) fmt.Printf("type of b:%T\n", b)}

输出:

type of a:main.NewInttype of b:int

结果显示a的类型是main.NewInt,表示main包下定义的NewInt类型。b的类型是intMyInt类型只会在代码中存在,编译完成时并不会有MyInt类型。

结构体

结构体由一系列命名的元素组成,这些元素又被称为字段,每个字段都有一个名称和一个类型。

结构体的目的就是把数据聚集在一起,以便能够更加便捷地操作这些数据。结构体的概念在 C 语言里很常见,被称为struct。Go 中的结构体也是 struct。Go 语言中没有类的概念,因此在 Go 中结构体有着更为重要的地位。结构体是复合类型(composite types),当需要定义一个类型,它由一系列属性组成,每个属性都有自己的类型和值的时候,就应该使用结构体,它把数据聚集在一起。然后可以访问这些数据,就好像它是一个独立实体的一部分。结构体也是值类型,因此可以通过 new 函数来创建。

Go语言中通过struct来实现面向对象。

结构体的定义

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

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

其中:

类型名:标识自定义结构体的名称,在同一个包内不能重复。

字段名:表示结构体字段名。结构体中的字段名必须唯一。

字段类型:表示结构体字段的具体类型。

举个例子,我们定义一个person(人)结构体,代码如下:

type person struct {name string city string age int8}

同样类型的字段也可以写在一行:

type person1 struct {name, city string age int8}

这样我们就拥有了一个person的自定义类型,它有namecityage三个字段,分别表示姓名、城市和年龄。这样我们使用这个person结构体就能够很方便的在程序中表示和存储人信息了。

语言内置的基础数据类型是用来描述一个值的,而结构体是用来描述一组值的。比如一个人有名字、年龄和居住城市等,本质上是一种聚合型的数据类型。

结构体实例化

只有当结构体实例化时,才会真正地分配内存。也就是必须实例化后才能使用结构体的字段。

结构体本身也是一种类型,我们可以像声明内置类型一样使用var关键字声明结构体类型。

var 结构体实例 结构体类型

基本实例化

举个例子:

package mainimport ( "fmt")// 结构体type person struct {name, city string age int hobby[]string}func main() {var p person p.name = "蜘蛛侠" p.age = 18 p.hobby = []string{"打怪兽", "沟女"} p.city = "纽约" fmt.Println(p)}

运行:

{蜘蛛侠 纽约 18 [打怪兽 沟女]}

我们通过.来访问结构体的字段(成员变量),例如p.namep.age等。

匿名结构体

在定义一些临时数据结构等场景下还可以使用匿名结构体。

package mainimport ( "fmt")func main() {var user struct{Name string; Age int} user.Name = "蜘蛛侠" user.Age = 18 fmt.Printf("%#v\n", user)}

运行:

struct {Name string; Age int }{Name:"蜘蛛侠", Age:18}

创建指针类型结构体

我们还可以通过使用new关键字对结构体进行实例化,得到的是结构体的地址。格式如下:

var p2 = new(person)fmt.Printf("%T\n", p2)// *main.personfmt.Printf("p2=%#v\n", p2) // p2=&main.person{name:"", city:"", age:0}

从打印的结果中我们可以看出p2是一个结构体指针。

需要注意的是在Go语言中支持对结构体指针直接使用.来访问结构体的成员。

var p2 = new(person)p2.name = "蜘蛛侠"p2.age = 28p2.city = "纽约"fmt.Printf("p2=%#v\n", p2) // p2=&main.person{name:"蜘蛛侠", city:"纽约", age:28}

取结构体的地址实例化

使用&对结构体进行取地址操作相当于对该结构体类型进行了一次new实例化操作。取地址格式如下:

结构体实例:=&结构体类型{}

取地址实例化是最广泛的一种结构体实例化方式:

p3 := &person{}fmt.Printf("%T\n", p3)//*main.personfmt.Printf("p3=%#v\n", p3) //p3=&main.person{name:"", city:"", age:0}p3.name = "浩克"p3.age = 30p3.city = "华盛顿"fmt.Printf("p3=%#v\n", p3) //p3=&main.person{name:"浩克", city:"华盛顿", age:30}

p3.name = "浩克"其实在底层是(*p3).name = "浩克",这是Go语言帮我们实现的语法糖。

Go语言和C/C++

在 C/C++ 语言中,使用 new 实例化类型后,访问其成员变量时必须使用->操作符。

在Go语言中,访问结构体指针的成员变量时可以继续使用.,这是因为Go语言为了方便开发者访问结构体指针的成员变量,使用了语法糖(Syntactic sugar)技术。

结构体初始化

没有初始化的结构体,其成员变量都是对应其类型的零值。

结构体初始化的几种方式:

方式一:通过 var 声明结构体

type person struct {name string city string age int8}func main() {var p4 person fmt.Printf("p4=%#v\n", p4) // p4=main.person{name:"", city:"", age:0}}

在 Go 语言中当一个变量被声明的时候,系统会自动初始化它的默认值,比如 int 被初始化为 0,指针为 nil。

var 声明同样也会为结构体类型的数据分配内存,所以我们才能像上一段代码中那样,在声明了var p4 person之后就能直接给他的字段进行赋值。

方式二:使用 new

使用 new 函数给一个新的结构体变量分配内存,它返回指向已分配内存的指针:var t *T = new(T)

type struct1 struct {i1 int f1 float32 str string} func main() {ms := new(struct1) ms.i1 = 10 ms.f1 = 15.5 ms.str= "Chris"fmt.Printf("The int is: %d\n", ms.i1) fmt.Printf("The float is: %f\n", ms.f1) fmt.Printf("The string is: %s\n", ms.str) fmt.Println(ms)}

与面向对象语言相同,使用点操作符可以给字段赋值:structname.fieldname = value。同样的,使用点操作符可以获取结构体字段的值:structname.fieldname

方式三:使用字面量

type Person struct {name string age int address string} func main() {var p1 Person p1 = Person{"lisi", 30, "shanghai"} //方式A p2 := Person{address:"beijing", age:25, name:"wangwu"} //方式B p3 := Person{address:"NewYork"} //方式C}

在(方式A)中,值必须以字段在结构体定义时的顺序给出。(方式B)是在值前面加上了字段名和冒号,这种方式下值的顺序不必一致,并且某些字段还可以被忽略掉,就想(方式C)那样。除了上面这三种方式外,还有一种初始化结构体实体更简短和常用的方式,如下:

ms := &Person{"name", 20, "bj"}ms2 := &Person{name:"zhangsan"}

&Person{a, b, c}是一种简写,底层仍会调用new(),这里值的顺序必须按照字段顺序来写,同样它也可以使用在值前面加上字段名和冒号的写法。

表达式new(Type)&Type{}是等价的。

几种初始化方式之间的区别

到目前为止,我们已经了解了三种初始化结构体的方式:

//第一种,在Go语言中,可以直接以 var 的方式声明结构体即可完成实例化var t Tt.a = 1t.b = 2 //第二种,使用 new() 实例化t := new(T) //第三种,使用字面量初始化t := T{a, b}t := &T{} //等效于 new(T)

使用var t T会给 t 分配内存,并零值化内存,但是这个时候的 t 的类型是 T

使用 new 关键字时t := new(T),变量 t 则是一个指向 T 的指针。

从内存布局上来看,我们就能看出这三种初始化方式的区别:

使用 var 声明:

使用 new 初始化:

使用结构体字面量初始化:

下面来看一个具体的例子:

package mainimport "fmt" type Person struct {name string age int} func main() {var p1 Person p1.name = "zhangsan" p1.age = 18 fmt.Printf("This is %s, %d years old\n", p1.name, p1.age) p2 := new(Person) p2.name = "lisi" p2.age = 20 (*p2).age = 23 //这种写法也是合法的 fmt.Printf("This is %s, %d years old\n", p2.name, p2.age) p3 := Person{"wangwu", 25} fmt.Printf("This is %s, %d years old\n", p3.name, p3.age)}

输出:

This is zhangsan, 18 years oldThis is lisi, 23 years oldThis is wangwu, 25 years old

上面例子的第二种情况,虽然 p2 是指针类型,但我们仍然可以像p2.age = 23这样赋值,不需要像 C++ 中那样使用->操作符,Go 会自动进行转换。

注意也可以先通过*操作符来获取指针所指向的内容,再进行赋值:(*p2).age = 23

结构体内存布局

Go 语言中,结构体和它所包含的数据在内存中是以连续块的形式存在的,即使结构体中嵌套有其他的结构体,这在性能上带来了很大的优势。不像 Java 中的引用类型,一个对象和它里面包含的对象可能会在不同的内存空间中,这点和 Go 语言中的指针很像。下面的例子清晰地说明了这些情况:

type Rect1 struct {Min, Max Point }type Rect2 struct {Min, Max *Point }

空结构体

空结构体是不占用空间的。

var v struct{}fmt.Println(unsafe.Sizeof(v)) // 0

构造函数

Go语言的结构体没有构造函数,我们可以自己实现。例如,下方的代码就实现了一个person的构造函数。因为struct是值类型,如果结构体比较复杂的话,值拷贝性能开销会比较大,所以该构造函数返回的是结构体指针类型

func newPerson(name, city string, age int8) *person {return &person{name: name, city: city, age: age, }}

调用构造函数:

p9 := newPerson("蜘蛛侠", "纽约", 18)fmt.Printf("%#v\n", p9) //&main.person{name:"蜘蛛侠", city:"纽约", age:18}

方法和接收者

Go语言中的方法(Method)是一种作用于特定类型变量的函数。这种特定类型变量叫做接收者(Receiver)。接收者的概念就类似于其他语言中的this或者self

方法的定义格式如下:

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

其中,

接收者变量:接收者中的参数变量名在命名时,官方建议使用接收者类型名称首字母的小写,而不是selfthis之类的命名。例如,Person类型的接收者变量应该命名为pConnector类型的接收者变量应该命名为c等。

接收者类型:接收者类型和参数类似,可以是指针类型和非指针类型。

方法名、参数列表、返回参数:具体格式与函数定义相同。

举个例子:

// Person 结构体type Person struct {name string age int8}// NewPerson 构造函数func NewPerson(name string, age int8) *Person {return &Person{name: name, age: age, }}// Dream Person做梦的方法func (p Person) Dream() {fmt.Printf("%s的梦想是学好Go语言!\n", p.name)}func main() {p1 := NewPerson("奇异博士", 25) p1.Dream()}

方法与函数的区别是,函数不属于任何类型,方法属于特定的类型。

指针类型的接收者

指针类型的接收者由一个结构体的指针组成,由于指针的特性,调用方法时修改接收者指针的任意成员变量,在方法结束后,修改都是有效的。这种方式就十分接近于其他语言中面向对象中的this或者self。例如我们为Person添加一个SetAge方法,来修改实例变量的年龄。

// SetAge 设置p的年龄// 使用指针接收者func (p *Person) SetAge(newAge int8) {p.age = newAge}

调用该方法:

func main() {p1 := NewPerson("蜘蛛侠", 25) fmt.Println(p1.age) // 25 p1.SetAge(30) fmt.Println(p1.age) // 30}

值类型的接收者

当方法作用于值类型接收者时,Go语言会在代码运行时将接收者的值复制一份。在值类型接收者的方法中可以获取接收者的成员值,但修改操作只是针对副本,无法修改接收者变量本身。

// SetAge2 设置p的年龄// 使用值接收者func (p Person) SetAge2(newAge int8) {p.age = newAge}func main() {p1 := NewPerson("蜘蛛侠", 25) p1.Dream() fmt.Println(p1.age) // 25 p1.SetAge2(30) // (*p1).SetAge2(30) fmt.Println(p1.age) // 25}

什么时候应该使用指针类型接收者

需要修改接收者中的值

接收者是拷贝代价比较大的大对象

保证一致性,如果有某个方法使用了指针接收者,那么其他的方法也应该使用指针接收者。

任意类型添加方法

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

//MyInt 将int定义为自定义MyInt类型type MyInt int//SayHello 为MyInt添加一个SayHello的方法func (m MyInt) SayHello() {fmt.Println("Hello, 我是一个int。")}func main() {var m1 MyInt m1.SayHello() //Hello, 我是一个int。 m1 = 100 fmt.Printf("%#v %T\n", m1, m1) //100 main.MyInt}

注意事项:非本地类型不能定义方法,也就是说我们不能给别的包的类型定义方法。

结构体的匿名字段

结构体允许其成员字段在声明时没有字段名而只有类型,这种没有名字的字段就称为匿名字段。

// Person 结构体Person类型type Person struct {string int}func main() {p1 := Person{"蜘蛛侠", 18, } fmt.Printf("%#v\n", p1) //main.Person{string:"纽约", int:18} fmt.Println(p1.string, p1.int) //纽约 18}

注意:这里匿名字段的说法并不代表没有字段名,而是默认会采用类型名作为字段名,结构体要求字段名称必须唯一,因此一个结构体中同种类型的匿名字段只能有一个。

嵌套结构体

一个结构体中可以嵌套包含另一个结构体或结构体指针,就像下面的示例代码那样。

//Address 地址结构体type Address struct {Province string Citystring}//User 用户结构体type User struct {Name string Gender string Address Address}func main() {user1 := User{Name: "蜘蛛侠", Gender: "男", Address: Address{Province: "纽约",City:"华盛顿", }, } fmt.Printf("user1=%#v\n", user1)//user1=main.User{Name:"蜘蛛侠", Gender:"男", Address:main.Address{Province:"纽约", City:"华盛顿"}}}

嵌套匿名字段

上面user结构体中嵌套的Address结构体也可以采用匿名字段的方式,例如:

// Address 地址结构体type Address struct {Province string Citystring}//User 用户结构体type User struct {Name string Gender string Address //匿名字段}func main() {var user2 User user2.Name = "蜘蛛侠" user2.Gender = "男" user2.Address.Province = "纽约" // 匿名字段默认使用类型名作为字段名 user2.City = "华盛顿"// 匿名字段可以省略 fmt.Printf("user2=%#v\n", user2) //user2=main.User{Name:"蜘蛛侠", Gender:"男", Address:main.Address{Province:"纽约", City:"华盛顿"}}}

当访问结构体成员时会先在结构体中查找该字段,找不到再去嵌套的匿名字段中查找。

嵌套结构体的字段名冲突

嵌套结构体内部可能存在相同的字段名。在这种情况下为了避免歧义需要通过指定具体的内嵌结构体字段名。

//Address 地址结构体type Address struct {Province string City string CreateTime string}//Email 邮箱结构体type Email struct {Account string CreateTime string}//User 用户结构体type User struct {Name string Gender string Address Email}func main() {var user3 User user3.Name = "钢铁侠" user3.Gender = "男" // user3.CreateTime = "" //ambiguous selector user3.CreateTime user3.Address.CreateTime = "2000" //指定Address结构体中的CreateTime user3.Email.CreateTime = "2000" //指定Email结构体中的CreateTime}

结构体的“继承”

Go语言中使用结构体也可以实现其他编程语言中面向对象的继承。

//Animal 动物type Animal struct {name string}func (a *Animal) move() {fmt.Printf("%s会动!\n", a.name)}//Dog 狗type Dog struct {Feet int8 *Animal //通过嵌套匿名结构体实现继承}func (d *Dog) wang() {fmt.Printf("%s会汪汪汪~\n", d.name)}func main() {d1 := &Dog{Feet: 4, Animal: &Animal{ //注意嵌套的是结构体指针name: "乐乐", }, } d1.wang() //乐乐会汪汪汪~ d1.move() //乐乐会动!}

结构体字段的可见性

结构体中字段大写开头表示可公开访问,小写表示私有(仅在定义当前结构体的包中可访问)。

结构体与JSON序列化

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

结构体标签(Tag)

Tag是结构体的元信息,可以在运行的时候通过反射的机制读取出来。Tag在结构体字段的后方定义,由一对反引号包裹起来,具体的格式如下:

`key1:"value1" key2:"value2"`

结构体tag由一个或多个键值对组成。键与值使用冒号分隔,值用双引号括起来。同一个结构体字段可以设置多个键值对tag,不同的键值对之间使用空格分隔。

注意事项:为结构体编写Tag时,必须严格遵守键值对的规则。结构体标签的解析代码的容错能力很差,一旦格式写错,编译和运行时都不会提示任何错误,通过反射也无法正确取值。例如不要在key和value之间添加空格。

例如我们为Student结构体的每个字段定义json序列化时使用的Tag:

package mainimport ( "encoding/json" "fmt")/* 结构体与 json Go 语言中结构体的变量 -> json 格式的字符串(序列化) json 格式的字符串 -> Go 语言中结构体的变量(反序列化)*/type person struct {// 结构体中字段大写开头表示可公开访问,小写表示私有(仅在定义当前结构体的包中可访问)。 // 此处如果把结构体 person 传到 json.Marshal(p1) 方法中去,该方法无法获取到字段,因为是私有的。 // name string // age int // 如果别人接收我们发送的 json 中,别人就是不想要我们字段的首字母大写,我们可以给其制定一下Tag。 Name string `json:"name"` Age int `json:"age"`}func main() {p1 := person{Name: "蜘蛛侠", Age: 18, } // 序列化方法:json.Marshal(value) result, err := json.Marshal(p1) if err != nil {fmt.Printf("Marshal fail, err: %v\n", err) return } fmt.Printf("%v\n", string(result)) // 反序列化方法:json.Unmarshal([]byte, value) // 第一个参数:[]byte - 传进来的 json 字符串 // 第二个参数:value - 要传到哪里去,用一个结构体接收(注意此处要传递结构体的指针) str := `{"name":"蜘蛛侠","age":18}` var p2 person json.Unmarshal([]byte(str), &p2) // 传指针是为了能在 json.Unmarshal() 方法内部去修改 p2 的值 fmt.Printf("%#v\n", p2)}

结构体和方法补充知识点

因为slice和map这两种数据类型都包含了指向底层数据的指针,因此我们在需要复制它们时要特别注意。我们来看下面的例子:

type Person struct {name string age int8 dreams []string}func (p *Person) SetDreams(dreams []string) {p.dreams = dreams}func main() {p1 := Person{name: "钢铁侠", age: 18} data := []string{"吃饭", "睡觉", "打灭霸"} p1.SetDreams(data) // 你真的想要修改 p1.dreams 吗? data[1] = "不睡觉" fmt.Println(p1.dreams) // ?}

正确的做法是在方法中使用传入的slice的拷贝进行结构体赋值。

func (p *Person) SetDreams(dreams []string) {p.dreams = make([]string, len(dreams)) copy(p.dreams, dreams)}

同样的问题也存在于返回值slice和map的情况,在实际编码过程中一定要注意这个问题。

总结

这个程序将包含所有结构体的知识点:

使用“面向对象”的思维方式编写一个学生信息管理系统。

学生有id、姓名、年龄、分数等信息

程序提供展示学生列表、添加学生、编辑学生信息、删除学生等功能

该实现有两种版本,我们先来看看函数版的解法:

package mainimport ( "fmt" "os")/* 函数版学生管理系统 功能:查看、新增、删除学生*/var ( allStudents map[int64]*student // 声明一个存放 student 的变量)type student struct {id int64 name string}// student 构造体的构造函数func newStudent(id int64, name string) *student {return &student{id: id, name: name, }}func showAllStudents() {// 把所有的学生打印出来 for k, v := range allStudents {fmt.Printf("学号:%d | 姓名:%s\n", k, v.name) }}func addStudent() {// 向 allStudents 中添加一个学生 // 1.创建一个新学生 var ( id int64 name string ) fmt.Print("请输入学生学号:") fmt.Scanln(&id) fmt.Print("请输入学生姓名:") fmt.Scanln(&name) // 调用 student 的构造函数 newStu := newStudent(id, name) // 2.追加到 allStudents 中 allStudents[id] = newStu}func delStudent() {// 根据学号删除 allStudents 中的一个学生 var ( deleteID int64 ) fmt.Print("请输入要删除的学生的学号:") fmt.Scanln(&deleteID) delete(allStudents, deleteID)}func main() {allStudents = make(map[int64]*student, 50) // 初始化 for {// 1.打印菜单 fmt.Println("欢迎光临学生管理系统") fmt.Println(`1.查看所有学生2.新增学生3.删除学生4.退出 `) fmt.Print("请输入你的操作:") // 2.等待用户选择要做什么 var choice int fmt.Scanln(&choice) fmt.Printf("你选择了第 %d 项\n", choice) // 3.执行对应函数 switch choice {case 1:showAllStudents() case 2:addStudent() case 3:delStudent() case 4:os.Exit(1) default:fmt.Println("净整些没用的") } }}

再来看看结构体版的解法:

package main/* 结构体版学生管理系统*/// 学生结构体type student struct {id int64 name string}// 学生管理者结构体type studentManager struct {allStudents map[string]student}// 查看学生方法func (s studentManager) showStudents() {}// 增加学生方法func (s studentManager) addStudent() {}// 修改学生方法func (s studentManager) editStudent() {}// 删除学生方法func (s studentManager) delStudent() {}func main() {}

我们的解题思路是大概是这样,这个架子先搭起来。

但是我发现把所有的这些方法都写在一个文件里,着实太乱了。不好维护,我们把它们拆成不同的文件实现:

我把对学生操作的方法单独用一个文件来写:

main.go

package mainimport ( "fmt" "os")/* 结构体版学生管理系统*/var sm studentManager // 声明一个全局的学生管理者对象// 菜单函数func showMenu() {fmt.Println("******欢迎光临学生管理系统******") fmt.Println(`1、查看所有学生2、添加学生3、修改学生4、删除学生5、退出 `)}func main() {// 修改全局的那个变量 sm = studentManager{allStudents: make(map[int64]student, 100), } for {showMenu() // 等待用户输入 fmt.Print("请输入要执行的操作:") var choice int fmt.Scanln(&choice) fmt.Println("你输入的是:", choice) switch choice {case 1:sm.showStudents() case 2:sm.addStudent() case 3:sm.editStudent() case 4:sm.delStudent() case 5:os.Exit(1) default:fmt.Println("是不是傻?") } }}

student_manager.go

package mainimport "fmt"// 学生结构体type student struct {id int64 name string}// 学生管理者结构体type studentManager struct {allStudents map[int64]student}// 查看学生方法func (s studentManager) showStudents() {// 从 map 中把所有的学生遍历出来 // stu 是具体每个学生 for _, stu := range s.allStudents {fmt.Printf("学号:%d,姓名:%s\n", stu.id, stu.name) }}// 增加学生方法func (s studentManager) addStudent() {// 根据用户输入的内容创建一个新的学生 var ( stuID int64 stuName string ) // 获取用户输入 fmt.Print("请输入学生学号:") fmt.Scanln(&stuID) fmt.Print("请输入学生姓名:") fmt.Scanln(&stuName) // 根据用户输入创建结构体对象 newStu := student{id: stuID, name: stuName, } // 把新学生结构体放入 map 中 s.allStudents[newStu.id] = newStu fmt.Println("添加成功")}// 修改学生方法func (s studentManager) editStudent() {// 获取用户输入的学号 var stuID int64 fmt.Print("请输入学生学号:") fmt.Scanln(&stuID) // 展示学号对应的学生信息,如果没有提示查无此人 stuObj, ok := s.allStudents[stuID] // ok = true 表示有这个key if !ok {fmt.Println("查无此人") return } fmt.Printf("学生信息:\n学号:%d,姓名:%s\n", stuObj.id, stuObj.name) // 输入修改的学生名 fmt.Print("请输入学生的新名字:") var newName string fmt.Scanln(&newName) // 更新学生姓名 stuObj.name = newName s.allStudents[stuID] = stuObj // 更新 map 中的学生信息 fmt.Println("修改成功")}// 删除学生方法func (s studentManager) delStudent() {// 输入学生的学号 fmt.Print("请输入要删除的学生学号:") var stuID int64 fmt.Scanln(&stuID) // 查询学生的信息,不存在则返回查无此人 _, ok := s.allStudents[stuID] if !ok {fmt.Println("查无此人") return } // 存在则在 map 中将学生信息删除 delete(s.allStudents, stuID) fmt.Println("删除成功")}

里面的每一步我都注释了,应该就是这样的解题思路。

如果觉得《go var type 互转_Go语言学习笔记(第九章) 结构体》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。