简单易懂,解析Go语言中的struct结构体

目录

  • 4. struct 结构体
    • 4.1 初始化
    • 4.2 内嵌字段
    • 4.3 可见性
    • 4.4 方法与函数
      • 4.4.1 区别
      • 4.4.2 闭包
    • 4.5 Tag 字段标签
      • 4.5.1定义
      • 4.5.2 Tag规范
      • 4.5.3 Tag意义

4. struct 结构体

go的结构体类似于其他语言中的class,主要区别就是go的结构体没有继承这一概念,但可以使用类型嵌入来实现相似功能。

4.1 初始化

使用type关键字来定义一个新的类型,struct将新类型限定为结构体类型。

结构体中的字段可以为任何类型,但是包含一些特殊类型 如:接口,管道,函数,指针的时候要格外注意

//type定义一个新类型
type newInt int//type定义一个简单的结构体
type base struct{value int   
}//type定义一个复杂的结构体
type student struct {Name stringage  intc    interface{}d    func() inte    func()base     //将base类型嵌入到了student类型中
}

4.2 内嵌字段

内嵌字段大体上有两种方式:显式指定(m1)和隐式指定(m2)

  • 显式指定就相当于把目标结构体当作字段,调用时需要先调用这个字段,在调用目标结构体中的信息
  • 隐式指定相当于把目标结构体中的所有字段都在新结构体中创建了一次,并且指向嵌入结构体内部。同时创建同名嵌入结构体对象[指与base同名]
  • 显式创建同名结构体字段 ≠ 隐式指定
type base struct {Value int
}
//显式指定
type m1 struct {b base
}//隐式指定
type m2 struct {base
}//显式指定同名字段
type m3 struct {base base
}

对上述结构体进行调用:

  • 只有隐式指定直接操作被嵌入结构体内的数据;
  • 隐式指定后,直接操作嵌入结构体中的数据和通同名结构体操作作用一样
func main() {a1 := m1{}a2 := m2{}a3 := m3{}//显式指定只能通过嵌入结构体进行操作// a1.Value = 1 //a1.Value undefined (type m2 has no field or method Value)a1.b.Value = 2//隐式指定两种操作数据方法操作的是同一个变量a2.Value = 2a2.base.Value = 3fmt.Println(a2.Value) //3//显式指定同名变量 ≠ 隐式指定 // a3.Value = 3 //a3.Value undefined (type m3 has no field or method Value)a3.base.Value = 4
}

当内嵌字段中的字段与结构体中得字段同名时:

  • 直接调用时是指定当前结构体中显式定义的字段,但嵌入结构体中的字段仍可通过嵌入类型进行调用
  • 方法同理
//数据
func main() {a1 := m1{}a1.Value = "hello world"a1.base.Value = 1fmt.Println(a1)//获取a1中的所有字段类型t := reflect.TypeOf(a1)for i := 0; i < t.NumField(); i++ {field := t.Field(i)fieldType := field.Typefmt.Printf("Field: %s, Type: %s\n", field.Name, fieldType.Name())}
}type base struct {Value int
}type m1 struct {Value stringbase
}
/*
{hello world {1}}
Field: Value, Type: string
Field: base, Type: base*/
//方法
func main() {a1 := m1{}a1.test()a1.base.test()fmt.Println(a1)//获取a1中的所有字段类型t := reflect.TypeOf(a1)for i := 0; i < t.NumField(); i++ {field := t.Field(i)fieldType := field.Typefmt.Printf("Field: %s, Type: %s\n", field.Name, fieldType.Name())}
}type base struct {Value int
}func (b *base) test() {b.Value = 1
}
func (m *m1) test() {m.Value = "hello world"
}type m1 struct {Value stringbase
}
/*
{hello world {1}}
Field: Value, Type: string
Field: base, Type: base*/

4.3 可见性

  • 首字母大写表示该字段/方法/结构体为可导出的,反之为不可导出的
  • 在同一个包内,不区分是否可导出,都可访问;包外只能访问可导出的字段/方法/结构体
  • 不可导出的字段不可以进行序列化(转化为json)
  • 可通过可导出的方法去操作不可导出的字段
// test/test1.go
package testtype User struct {Name stringage  int
}func (u *User) Test() {u.Name = "hello"u.age = 18
}func (u *User) test() {u.Name = "world"u.age = 81
}type student struct {Name stringage  int
}
//main.go
package mainimport ("fmt""test/test"
)func main() {a := test.User{}//b := test.student{} // 不能在包外访问未导出结构体a.Name = "123"//a.age = 123 // 不能在包外访问未导出字段a.Test()//a.test() // 不能在包外访问未导出方法fmt.Println(a)/*{hello 18}*/
}

4.4 方法与函数

4.4.1 区别

  • 方法定义是必须有一个接收器(receiver);函数不需要
  • 大部分情况下,方法的调用需要有一个对象;函数不需要
  • 由于go中的所有传递都是值传递,也就是将数据复制一份再调用,所以如果想要修改原本对象的值,就要传递指针,进行引用传递
func 函数名 (参数) 返回值类型 {函数体} //函数定义
func (接收器) 方法名  (参数) 返回值类型 {函数体} // 方法定义
func main() {a := User{}a.setName() // 方法调用fmt.Println(a)setName(&a) // 函数调用fmt.Println(a)
}
/*{world 0}{hello 0}*/type User struct {Name stringage  int
}
//函数
func setName(u *User) {u.Name = "hello"
}
//方法
func (u *User) setName() {u.Name = "world"
}

值传递时可以通过返回值的方式修改目标对象

func main() {a := User{}a = a.setName() //需要显式给原变量赋值fmt.Println(a)a = setName(a)  //需要显式给原变量赋值fmt.Println(a)
}type User struct {Name stringage  int
}func setName(u User) User {u.Name = "hello"return u
}
func (u User) setName() User {u.Name = "world"return u
}

4.4.2 闭包

说到函数和方法,就必须说一下闭包

什么是闭包?

简单来说,就是函数内部引用函数外部变量,导致变量生命周期发生变化。这样的函数就叫做闭包

常见于函数返回值为另一个函数时

package mainimport "fmt"func main() {b := test()fmt.Println(b())fmt.Println(b())
}func test() func() int {a := 1return func() int {a++return a}
}

上面的函数导致变量a无法正常释放,导致变量逃逸

go build -gcflags="-m" main.go
# command-line-arguments
./main.go:11:6: can inline test
./main.go:13:9: can inline test.func1
./main.go:6:11: inlining call to test
./main.go:13:9: can inline main.test.func1
./main.go:7:15: inlining call to main.test.func1
./main.go:7:13: inlining call to fmt.Println
./main.go:8:15: inlining call to main.test.func1
./main.go:8:13: inlining call to fmt.Println
./main.go:6:11: func literal does not escape
./main.go:7:13: ... argument does not escape
./main.go:7:15: ~R0 escapes to heap
./main.go:8:13: ... argument does not escape
./main.go:8:15: ~R0 escapes to heap
./main.go:12:2: moved to heap: a
./main.go:13:9: func literal escapes to heap

4.5 Tag 字段标签

4.5.1定义

在reflect包中提供了获取字段名称、类型、Tag的方法(上文展示过获取名称和类型)

结构体StructField表示结构体的一个字段(reflect/type.go)

// A StructField describes a single field in a struct.
type StructField struct {// Name is the field name.Name string// PkgPath is the package path that qualifies a lower case (unexported)// field name. It is empty for upper case (exported) field names.// See https://golang.org/ref/spec#Uniqueness_of_identifiersPkgPath stringType      Type      // field typeTag       StructTag // field tag stringOffset    uintptr   // offset within struct, in bytesIndex     []int     // index sequence for Type.FieldByIndexAnonymous bool      // is an embedded field
}type StructTag string 

4.5.2 Tag规范

StructTag本质上就是字符串,理论上任何形式都符合规范。但通常情况下约定,Tag的格式应该是key:“value”

  • key:非空字符串,不能包含控制字符,空格,引号,冒号
  • value:双引号包围的字符串
  • 冒号前后不能有空格,多个value用逗号隔开,key之间用空格隔开
  • key一般表示用途,value表示控制指令;

4.5.3 Tag意义

  • Go语言反射机制可以给结构体成员赋值,用Tag可以决定赋值的动作
  • 可以使用定义好的Tag规则,参考规则就可以继续不同的操作
//仅对Tag值为true的字段赋值(Tag决定赋值动作)
type Person struct {Name string `assign:"true"`Age  int    `assign:"false"`
}func assignValues(v interface{}) {val := reflect.ValueOf(v).Elem() // 获取指针指向的值typ := val.Type()for i := 0; i < val.NumField(); i++ {field := val.Field(i)tag := typ.Field(i).Tag.Get("assign") // 获取字段的tagif tag == "true" {// 根据字段类型赋值switch field.Kind() {case reflect.String:field.SetString("Default Name")case reflect.Int:field.SetInt(25)}}}
}func main() {p := &Person{}assignValues(p)fmt.Printf("Person: %+v\n", p)
}
/*
Person: &{Name:Default Name Age:0}*/

下方例子使用了json:“kind,omitempty”,这个tag规定了字段为空不进行序列化;

import ("encoding/json""fmt"
)type Person struct {Name  string `json:"kind,omitempty"` //为空时不进行序列化Value intage   int
}func main() {// 创建一个 Person 实例p := Person{Name: "", Value: 100, age: 100}// 序列化为 JSONjsonData, _ := json.Marshal(p)fmt.Println(string(jsonData)) /*{"Value":100}*/
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/24227.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构(陈越,何钦铭) 第四讲 树(中)

4.1 二叉搜索树 4.1.1 二叉搜索树及查找 Position Find(ElementTyoe X,BinTree BST){if(!BST){return NULL;}if(X>BST->Data){return Find(X,BST->Right)}else if(X<BST->Data){return Find(X,BST->Left)}else{return BST;} } Position IterFind(ElementTyp…

网络空间安全(1)web应用程序的发展历程

前言 Web应用程序的发展历程是一部技术创新与社会变革交织的长卷&#xff0c;从简单的文档共享系统到如今复杂、交互式、数据驱动的平台&#xff0c;经历了多个重要阶段。 一、起源与初期发展&#xff08;1989-1995年&#xff09; Web的诞生&#xff1a; 1989年&#xff0c;欧洲…

【Matlab仿真】Matlab Function中如何使用静态变量?

背景 根据Simulink的运行机制&#xff0c;每个采样点会调用一次MATLAB Function的函数&#xff0c;两次调用之间&#xff0c;同一个变量的前次计算的终值如何传递到当前计算周期来&#xff1f;其实可以使用persistent变量实现函数退出和进入时内部变量值的保持。 persistent变…

网络安全 linux学习计划 linux网络安全精要

2.使用命令行 文件系统层次标准&#xff08;FHS&#xff09;是一个文件和目录在Unix和Linux操作系统上面应该如何存储的定义。 /bin 重要的二进制可执行程序/boot 与系统启动有关的文件/etc 系统配置文件/home 普通用户家目录/lib 重要的系统库/media 可移动介质的挂载路径/m…

基于SSM的《计算机网络》题库管理系统(源码+lw+部署文档+讲解),源码可白嫖!

摘 要 《计算机网络》题库管理系统是一种新颖的考试管理模式&#xff0c;因为系统是用Java技术进行开发。系统分为三个用户进行登录并操作&#xff0c;分别是管理员、教师和学生。教师在系统后台新增试题和试卷&#xff0c;学生进行在线考试&#xff0c;还能对考生记录、错题…

Pretraining Language Models with Text-Attributed Heterogeneous Graphs

Pretraining Language Models with Text-Attributed Heterogeneous Graphs EMNLP 推荐指数&#xff1a;#paper/⭐⭐#​ 贡献&#xff1a; 我们研究了在更复杂的数据结构上预训练LM的问题&#xff0c;即&#xff0c;TAHG。与大多数只能从每个节点的文本描述中学习的PLM不同&…

DeepSeek 提示词:基础结构

&#x1f9d1; 博主简介&#xff1a;CSDN博客专家&#xff0c;历代文学网&#xff08;PC端可以访问&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移动端可微信小程序搜索“历代文学”&#xff09;总架构师&#xff0c;15年工作经验&#xff0c;精通Java编…

2025-02-25 学习记录--C/C++-用C语言实现删除字符串中的子串

用C语言实现删除字符串中的子串 在C语言中&#xff0c;你可以使用strstr函数来查找子串&#xff0c;然后用memmove或strcpy来覆盖或删除找到的子串。 一、举例 &#x1f430; #include <stdio.h> // 包含标准输入输出库&#xff0c;用于使用 printf 函数 #include <s…

Python入门12:面向对象的三大特征与高级特性详解

面向对象编程&#xff08;OOP&#xff09;是Python编程中非常重要的一部分&#xff0c;它通过封装、继承和多态这三大特征&#xff0c;帮助我们更好地组织和管理代码。除此之外&#xff0c;Python还提供了一些其他特性&#xff0c;如类属性、类方法和静态方法&#xff0c;进一步…

对计算机中缓存的理解和使用Redis作为缓存

使用Redis作为缓存缓存例子缓存的引入 Redis缓存的实现 使用Redis作为缓存 缓存 ​什么是缓存&#xff0c;第一次接触这个东西是在考研学习408的时候&#xff0c;计算机组成原理里面学习到Cache缓存&#xff0c;用于降低由于内存和CPU的速度的差异带来的延迟。它是在CPU和内存…

音视频入门基础:RTP专题(12)——RTP中的NAL Unit Type简介

一、引言 RTP封装H.264时&#xff0c;RTP对NALU Header的nal_unit_type附加了扩展含义。 由《音视频入门基础&#xff1a;H.264专题&#xff08;4&#xff09;——NALU Header&#xff1a;forbidden_zero_bit、nal_ref_idc、nal_unit_type简介》可以知道&#xff0c;nal_unit…

Linux 驱动入门(6)—— IRDA(红外遥控模块)驱动

文章目录 一、编译替换内核和设备树二、IRDA&#xff08;红外遥控模块&#xff09;1. 红外遥控简介2. 红外遥控器协议3. 编程思路 三、驱动代码1. GPIO 实现1.1 驱动层代码1.2 应用层代码 2. 设备树实现2.1 修改设备树2.2 驱动层代码2.3 应用层代码 3. 上机测试 一、编译替换内…

QSNCTF-WEB做题记录(2)

[第一章 web入门]常见的搜集 来自 <天狩CTF竞赛平台> 1&#xff0c;首先就是对网站进行目录枚举爆破 dirsearch -u http://challenge.qsnctf.com:31616 -x 404,403 得到如下的目录&#xff0c;分别查看一下内容 /.DS_Store /inde…

「软件设计模式」责任链模式(Chain of Responsibility)

深入解析责任链模式&#xff1a;用C打造灵活的请求处理链 引言&#xff1a;当审批流程遇上设计模式 在软件系统中&#xff0c;我们经常会遇到这样的场景&#xff1a;一个请求需要经过多个处理节点的判断&#xff0c;每个节点都有权决定是否处理或传递请求。就像企业的请假审批…

Ocelot 请求聚合

请求聚合 当下游服务是返回404状态码&#xff0c;在返回结果中&#xff0c;其对应的值则为空值&#xff0c; 即使聚合路由中所有的下游服务都返回404状态码&#xff0c;聚合路由的返回结果也不会是404状态码。 Ocelot允许你声明聚合路由&#xff0c;这样你可以把多个正常的Ro…

MongoDB安装与配置 导入导出

1、MongoDB的安装 首先cd到目录 cd /usr/local/ 执行下载 wget -c https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-rhel80-7.0.7.tgz 解压文件 tar -xvf mongodb-linux-x86_64-rhel80-7.0.7.tgz 将解压后的“mongodb-linux-x86_64-rhel80-7.0.7”文件夹重命名…

Kotlin 知识点二 延迟初始化和密封类

对变量延迟初始化 Kotlin 语言的许多特性&#xff0c;包括变量不可变&#xff0c;变量不可为空&#xff0c;等等。这些特性 都是为了尽可能地保证程序安全而设计的&#xff0c;但是有些时候这些特性也会在编码时给我们带来不 少的麻烦。 比如&#xff0c;如果你的类中存在很多…

简单介绍 SSL 证书类型: DV、OV、EV 的区别

SSL证书类型DV、OV、EV 区别&#xff1a; DV(域名验证型)SSL证书 OV(组织验证型)SSL证书 EV(扩展验证型)SSL证书

深度解析SmartGBD助力Android音视频数据接入GB28181平台

在当今数字化时代&#xff0c;视频监控与音视频通信技术在各行各业的应用愈发广泛。GB28181协议作为中国国家标准&#xff0c;为视频监控设备的互联互通提供了规范&#xff0c;但在实际应用中&#xff0c;许多Android终端设备并不具备国标音视频能力&#xff0c;这限制了其在相…

1分钟用DeepSeek编写一个PDF转Word软件

一、引言 如今&#xff0c;在线工具的普及让PDF转Word成为了一个常见需求&#xff0c;常见的pdf转word工具有收费的wps&#xff0c;免费的有pdfgear&#xff0c;见下文&#xff1a; PDFgear:一款免费的PDF编辑、格式转化软件-CSDN博客 还有网上在线的免费pdf转word工具smallp…