欢迎来到尧图网

客户服务 关于我们

您的位置:首页 > 新闻 > 国际 > 【go语言】结构体

【go语言】结构体

2025/1/30 7:30:33 来源:https://blog.csdn.net/2301_77868664/article/details/145392425  浏览:    关键词:【go语言】结构体

一、type 关键字的用法

       在 go 语言中,type 关键字用于定义新的类型,他可以用来定义基础类型、结构体类型、接口类型、函数类型等。通过 type 关键字,我们可以为现有类型创建新的类型别名或者自定义新的类型。

1.1 类型别名

使用 type 可以为现有类型创建一个新的别名,使得这个别名与原始类型具有相同的底层实现。

package mainimport "fmt"// 为 int 类型创建别名
type MyInt intfunc main() {var a MyInt = 10fmt.Println(a) // 输出 10fmt.Println(int(a)) // 输出 10
}

       在这个例子中,MyIntint 类型的一个别名,它与 int 类型完全相同,可以用作 int 类型的值。

1.2 定义结构体类型

type 也可以用来定义结构体类型,这种类型用于存储多个字段(数据)。

package mainimport "fmt"// 定义一个结构体类型
type Person struct {Name stringAge  int
}func main() {// 创建一个 Person 类型的实例p := Person{Name: "Alice", Age: 30}fmt.Println(p) // 输出 {Alice 30}
}

1.3 定义函数类型

type 还可以用来定义函数类型,特别是在回调函数或接口方法中非常有用。

package mainimport "fmt"// 定义一个函数类型
type AddFunc func(a, b int) intfunc main() {// 使用函数类型var add AddFunc = func(a, b int) int {return a + b}fmt.Println(add(3, 5)) // 输出 8
}

       在这个例子中,AddFunc 是一个函数类型,它定义了一个接收两个 int 参数并返回一个 int 类型结果的函数。

1.4 定义接口类型

   type 还可以用来定义接口类型。接口类型定义了一组方法,任何类型只要实现了这些方法,就被认为实现了这个接口。

package mainimport "fmt"// 定义一个接口类型
type Speaker interface {Speak() string
}// 定义一个结构体类型
type Person struct {Name string
}// 实现接口方法
func (p Person) Speak() string {return "Hello, my name is " + p.Name
}func main() {var s Speaker = Person{Name: "Bob"}fmt.Println(s.Speak()) // 输出 Hello, my name is Bob
}

1.5 自定义类型(结构体或者基础类型)

可以使用 type 创建一个新的自定义类型,这种类型会与原类型相同,但可以拥有额外的行为。

package mainimport "fmt"// 定义一个新的自定义类型
type Celsius float64
type Fahrenheit float64// 定义转换函数
func CelsiusToFahrenheit(c Celsius) Fahrenheit {return Fahrenheit(c*9/5 + 32)
}func main() {var tempC Celsius = 25tempF := CelsiusToFahrenheit(tempC)fmt.Println(tempF) // 输出 77
}

type 关键字在 Go 语言中非常强大,能够用来:

  1. 为现有类型创建别名
  2. 定义结构体、接口等自定义复合类型
  3. 定义函数类型
  4. 自定义类型(基于现有的基本类型或复合类型)。

       这种灵活的类型定义方式使得 Go 语言的类型系统能够更好地支持多种编程风格,比如面向对象编程(通过接口)和函数式编程(通过函数类型)。

二、结构体的定义和初始化

       在 Go 语言中,结构体(struct)是一种复合数据类型,可以存储多个不同类型的数据。结构体用于将相关的变量组合在一起,通常用于表示现实世界中的对象。

2.1 结构体定义

结构体通过 type 关键字定义,结构体包含一组字段(field),每个字段都有一个名字和类型。

package mainimport "fmt"// 定义一个结构体类型
type Person struct {Name stringAge  int
}func main() {// 创建结构体实例p := Person{Name: "Alice", Age: 30}fmt.Println(p) // 输出 {Alice 30}
}

2.2 结构体初始化

2.2.1 命名字段初始化

       通过显式地指定字段名来初始化结构体。你可以指定某些字段,也可以省略某些字段(未指定字段会被默认初始化为零值)。

package mainimport "fmt"type Person struct {Name stringAge  int
}func main() {// 初始化时指定字段名p := Person{Name: "Bob", Age: 25}fmt.Println(p) // 输出 {Bob 25}
}

2.2.2 位置初始化

通过字段的顺序来初始化结构体,必须按照结构体字段的顺序传递参数。

package mainimport "fmt"type Person struct {Name stringAge  int
}func main() {// 按字段顺序初始化结构体p := Person{"Alice", 30}fmt.Println(p) // 输出 {Alice 30}
}

2.2.3 零值初始化

       如果没有显式地初始化结构体,则会给字段赋予类型的零值(比如 string 的零值是空字符串 ""int 的零值是 0)。

package mainimport "fmt"type Person struct {Name stringAge  int
}func main() {// 零值初始化var p Personfmt.Println(p) // 输出 { 0},Name 为空字符串,Age 为 0
}

2.2.4 使用 & 获取结构体的指针

可以通过 & 来获取结构体的指针,这样可以直接修改结构体的字段。

package mainimport "fmt"type Person struct {Name stringAge  int
}func main() {// 创建结构体的指针p := &Person{Name: "Tom", Age: 40}fmt.Println(*p) // 输出 {Tom 40}// 通过指针修改字段p.Age = 41fmt.Println(*p) // 输出 {Tom 41}
}

三、匿名结构体

       在 Go 语言中,匿名结构体是指没有定义类型名称的结构体。通常我们在需要快速定义结构体并使用时,会选择使用匿名结构体。匿名结构体适用于临时的、一时的结构体实例,而不需要为其命名类型。

3.1 匿名结构体的定义和使用

匿名结构体可以直接在代码中定义,并初始化其字段。它不需要像命名结构体那样定义类型名称。

package mainimport "fmt"func main() {// 创建一个匿名结构体并初始化person := struct {Name stringAge  int}{Name: "Alice",Age:  25,}// 输出匿名结构体实例fmt.Println(person) // 输出:{Alice 25}
}

在上面的代码中:

  • 我们定义了一个匿名结构体,结构体有两个字段:Namestring 类型)和 Ageint 类型)。
  • 然后,我们通过字面量初始化该结构体,直接赋值给变量 person

3.2 匿名结构体的优缺点

优点:
  1. 简洁:匿名结构体非常适合于那些只需要一次性使用的结构数据,不需要为其单独定义类型。
  2. 灵活性:如果你只需要在特定函数或方法中使用一个临时结构体,不必为此创建一个新的类型,可以直接用匿名结构体。
  3. 局部使用:匿名结构体通常用于局部变量,使用范围小,适合不需要重复引用的场景。
缺点:
  1. 可读性差:当匿名结构体嵌套或者与其他结构体一起使用时,可能会使代码的可读性下降,尤其是没有类型名称的情况下。
  2. 类型安全问题:匿名结构体缺少类型名称,因此不能像命名结构体那样明确区分不同类型的结构体。多个匿名结构体之间不能直接进行赋值或比较,除非它们完全相同。

3.3 匿名结构体和命名结构体的比较

  • 命名结构体:在代码中定义一个类型名,可以重复使用,也能在其他地方引用。
  • 匿名结构体:没有类型名称,只能在当前代码中使用,适合临时数据。

四、结构体嵌套

       在 Go 语言中,结构体嵌套是指在一个结构体中嵌套另一个结构体作为字段。通过结构体嵌套,可以将多个结构体组合在一起,从而形成更复杂的数据结构。这使得结构体能够具备更高的复用性和灵活性。

4.1 结构体嵌套的基本概念

       结构体嵌套可以让一个结构体包含另一个结构体作为它的字段,或者嵌套多个结构体。嵌套结构体通常用于描述包含子部分的数据模型。例如,表示一个 Person 可能有 Address 作为嵌套的字段。

4.2 结构体嵌套的匿名嵌套

       在 Go 语言中,结构体可以以匿名结构体的方式嵌套。这使得代码更加简洁。匿名结构体嵌套时,不需要在内嵌结构体中声明字段类型的名称。

package mainimport "fmt"// 定义 Person 结构体,嵌套匿名结构体
type Person struct {Name    stringAge     intAddress struct {City   stringState  stringZipCode string} // 匿名结构体
}func main() {// 创建并初始化 Person 结构体p := Person{Name: "Bob",Age:  40,}p.Address.City = "Los Angeles"p.Address.State = "CA"p.Address.ZipCode = "90001"fmt.Println(p) // 输出:{Bob 40 {Los Angeles CA 90001}}
}

4.3 结构体嵌套的用途

结构体嵌套的常见用途包括:

  • 表示复杂的数据模型:通过嵌套结构体来表示一对多或多对多的关系。
  • 代码复用:将共用的结构体嵌套在其他结构体中,避免重复代码。
  • 提升可维护性:通过结构体嵌套,可以将不同的逻辑模块拆分成独立的结构体,增强代码的可读性和维护性。

五、结构体定义方法

       在 Go 语言中,结构体(struct)是一种数据类型,可以用来组合不同类型的数据,表示一个实体。结构体的定义方法有两种:一种是使用 type 关键字定义,另一种是通过 struct 关键字直接声明。下面我会详细介绍这两种方法。

5.1 使用 type 关键字定义结构体

       通过 type 关键字,可以为结构体类型创建一个新的名称。定义后的结构体类型可以用来声明变量、函数参数和返回值等。

package mainimport "fmt"// 使用 type 定义结构体
type Person struct {Name stringAge  int
}func main() {// 创建结构体实例并初始化p := Person{Name: "Alice",Age:  30,}fmt.Println(p) // 输出:{Alice 30}
}

5.2 使用匿名结构体定义

       除了通过 type 定义结构体外,还可以直接在变量声明时定义匿名结构体类型。这种方式适用于那些不需要多次使用该结构体类型的情况。

package mainimport "fmt"func main() {// 直接定义匿名结构体p := struct {Name stringAge  int}{Name: "Bob",Age:  25,}fmt.Println(p) // 输出:{Bob 25}
}

5.3 结构体定义中字段的标签(Tag)

       Go 的结构体字段可以附加标签,标签通常用于提供元数据或在反射中使用。例如,数据库、JSON 或 XML 编码时,字段标签可以用来指定如何处理字段的名称或格式。

package mainimport ("fmt""reflect"
)// 定义一个结构体,使用字段标签
type Person struct {Name string `json:"name"`Age  int    `json:"age"`
}func main() {p := Person{Name: "Alice",Age:  30,}// 使用反射查看结构体标签t := reflect.TypeOf(p)for i := 0; i < t.NumField(); i++ {field := t.Field(i)fmt.Println(field.Name, field.Tag)}
}

六、结构体标签

       在 go 语言中,结构体标签是附加到结构体字段上的元数据,用于指定一些额外的信息。这些标签通常用于在序列化、反序列化、数据库映射等过程中对字段进行特别的处理。标签的主要用途是为第三方库提供额外的配置信息,通常这些标签不直接影响程序的逻辑,但是他们会影响如何处理结构体的字段。

6.1 标签的基本语法

       结构体标签由反引号包裹,通常位于结构体字段声明的后面。标签的格式为:tagKey:"tagValue"

type Person struct {Name string `json:"name"`Age  int    `json:"age"`
}

       在这个例子中,Name 字段和 Age 字段分别有 json 标签,指示它们在 JSON 编解码时使用的字段名。

6.2 常见的结构体标签用途

6.2.1 JSON 标签

Go 的标准库 encoding/json 使用结构体标签来控制 JSON 编解码的行为。

  • json 标签 用于指定结构体字段在 JSON 中的键名。
  • omitempty: 如果字段值为空,则忽略该字段(用于 JSON 编解码时)。
  • -: 忽略该字段,不进行 JSON 编解码。
type Person struct {Name  string `json:"name"`Age   int    `json:"age,omitempty"`Email string `json:"-"`
}

       在这个例子中,Email 字段不会被序列化为 JSON,因为它的标签是 json:"-"。如果 Age 为零值(0),它会被忽略。 

6.2.2 XML 标签

Go 的标准库 encoding/xml 使用 xml 标签来指定结构体字段在 XML 中的元素名称。

type Person struct {Name string `xml:"name"`Age  int    `xml:"age"`
}

在这个例子中,NameAge 字段将被编码为 XML 元素 <name><age>。 

6.2.3 数据库标签

       GORM 是 Go 的一个流行 ORM 库,它使用结构体标签来指定字段与数据库表字段之间的映射关系。

type Person struct {ID    int    `gorm:"primary_key"`Name  string `gorm:"column:name"`Age   int    `gorm:"column:age"`
}

       这里,gorm 标签指定了字段如何映射到数据库列,例如 ID 字段是主键,Name 字段映射为 name 列。 

6.2.4 自定义标签

       除了标准的 JSON、XML 和数据库标签外,开发者可以创建自定义标签来满足特定需求,比如表单验证、日志记录等。

type User struct {Username string `validate:"required"`Password string `validate:"min=8"`
}

在这个例子中,validate 标签可以用于执行字段值验证。 

6.3 读取结构体标签

       Go 提供了反射(reflect)功能,可以动态读取结构体的标签。这对于构建通用框架或库非常有用。

package mainimport ("fmt""reflect"
)type Person struct {Name string `json:"name" xml:"name"`Age  int    `json:"age" xml:"age"`
}func main() {p := Person{Name: "Alice", Age: 30}// 使用反射获取字段标签t := reflect.TypeOf(p)for i := 0; i < t.NumField(); i++ {field := t.Field(i)fmt.Printf("Field: %s, JSON Tag: %s, XML Tag: %s\n", field.Name, field.Tag.Get("json"), field.Tag.Get("xml"))}
}

6.4 多个标签

       一个字段可以有多个标签,它们之间用空格分隔。在处理这些标签时,每个库会按照它们的定义来解析标签。

type Person struct {Name  string `json:"name" xml:"name" gorm:"column:name"`Age   int    `json:"age" xml:"age"`
}

6.5 标签的常见选项

标签的具体内容取决于你使用的库和框架。以下是一些常见标签选项:

  • json 标签:

    • json:"name": 指定 JSON 中的字段名。
    • json:"-": 忽略该字段。
    • json:"omitempty": 如果字段值为零值(如 "" 或 0),则忽略该字段。
  • gorm 标签(用于数据库映射):

    • gorm:"primary_key": 标明字段是主键。
    • gorm:"not null": 字段不能为空。
    • gorm:"default:value": 设置字段的默认值。
  • xml 标签:

    • xml:"name": 指定 XML 中的元素名。

6.6 结构体标签的作用

       标签主要作用是为第三方库提供额外的信息,帮助它们在处理结构体时遵循某些规则。常见的使用场景包括:

  • JSON 或 XML 编解码时确定字段名的映射。
  • 数据库 ORM 框架中映射字段到数据库列。
  • 表单验证框架中使用标签来标识哪些字段需要验证。
  • 日志框架中用于标识哪些字段需要被记录。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com