下面的代码片段声明了一个结构类型MyStruct,其中包含字段Name和Score。名为myMap的映射具有字符串键和一个空接口作为创建的值。
示例代码:
package main
import (
"encoding/json"
"fmt"
)
type MyStruct struct {
Name string
Score int
}
func main() {
ms := MyStruct{Name: "John", Score: 34}
var myMap map[string]interface{}
data, _ := json.Marshal(ms)
json.Unmarshal(data, &myMap)
fmt.Println(myMap["Name"])
fmt.Println(myMap["Score"])
}
输出:
John
34
示例代码:
package main
import (
"fmt"
)
type Before struct {
m string
}
func append(b interface{}) interface{} {
return struct {
Before
n string
}{b.(Before), "rest"}
}
func main() {
b := Before{"test"}
a := append(b)
fmt.Println(a)
}
输出:
{{test} rest}
示例代码:
package main
import (
"fmt"
"math/rand"
)
type LuckyNumber struct {
number int
}
type Person struct {
lucky_numbers []LuckyNumber
}
func main() {
tmp := make([]LuckyNumber, 10)
for i := range tmp {
tmp[i].number = rand.Intn(100)
}
a := Person{tmp}
fmt.Println(a)
}
输出:
{[{81} {87} {47} {59} {81} {18} {25} {40} {56} {0}]}
下面的代码片段声明了具有字段A、B和C的结构类型Test。我们需要验证字段Z是否存在于结构类型Test中。
示例代码:
package main
import (
"log"
"reflect"
)
func main() {
type test struct {
A bool
B bool
C bool
}
v := new(test)
metaValue := reflect.ValueOf(v).Elem()
for _, name := range []string{"A", "C", "Z"} {
field := metaValue.FieldByName(name)
if field == (reflect.Value{}) {
log.Printf("Field %s not exist in struct", name)
}
}
}
输出:
2009/11/10 23:00:00 Field Z not exist in struct
示例代码:
package main
import "fmt"
type Emp struct {
x int
y []string
}
func main() {
var list = map[string]*Emp{"e1": {1001, []string{"John", "US"}}}
e := new(Emp)
e.x = 1002
e.y = []string{"Rock", "UK"}
list["e2"] = e
fmt.Println(list["e1"])
fmt.Println(list["e2"])
}
输出:
&{1001 [John US]}
&{1002 [Rock UK]}
示例代码:
package main
import (
"fmt"
)
type Widget struct {
id int
attrs []string
}
func main() {
widgets := []Widget{
Widget{
id: 10,
attrs: []string{"blah", "foo"},
},
Widget{
id: 11,
attrs: []string{"foo", "bar"},
},
Widget{
id: 12,
attrs: []string{"xyz"},
},
}
for _, j := range widgets {
fmt.Printf("%d ", j.id)
for _, y := range j.attrs {
fmt.Printf(" %s ", y)
}
fmt.Println()
}
}
输出:
10 blah foo
11 foo bar
12 xyz
包验证器根据标签对结构体和单个字段进行值验证。它具有以下独特的功能,可通过使用验证标记或自定义验证器进行跨字段和跨结构验证。切片、数组和map,允许验证多维字段的任何或所有级别。能够深入研究映射键和值以进行验证。
安装验证包:
go get github.com/go-playground/validator
示例代码:
package main
import (
"fmt"
"github.com/go-playground/validator"
)
// User contains user information
type User struct {
FirstName string `json:"fname" validate:"alpha"`
LastName string `json:"lname" validate:"alpha"`
Age uint8 `validate:"gte=20,lte=65"`
Email string `json:"e-mail" validate:"required,email"`
JoiningDate string `validate:"datetime"`
}
// use a single instance of Validate, it caches struct info
var validate *validator.Validate
func main() {
validate = validator.New()
user := &User{
FirstName: "Test25",
LastName: "Test",
Age: 75,
Email: "Badger.Smith@",
JoiningDate: "005-25-10",
}
err := validate.Struct(user)
if err != nil {
if _, ok := err.(*validator.InvalidValidationError); ok {
fmt.Println(err)
return
}
fmt.Println("------ List of tag fields with error ---------")
for _, err := range err.(validator.ValidationErrors) {
fmt.Println(err.StructField())
fmt.Println(err.ActualTag())
fmt.Println(err.Kind())
fmt.Println(err.Value())
fmt.Println(err.Param())
fmt.Println("---------------")
}
return
}
}
输出:
------ List of tag fields with error ---------
FirstName
alpha
string
Test25
---------------
Age
lte
uint8
75
65
---------------
Email
email
string
Badger.Smith@
---------------
JoiningDate
datetime
string
005-25-10
---------------
您将学习如何创建自己的包并导入自定义包。在此示例中,您将看到如何从另一个包或子包导入结构。您还可以从主包调用自定义包的函数。
以下是我们应用程序的目录结构。
├── family
│ ├── go.mod
│ ├── main.go
│ └── father
│ └── father.go
│ └── son
│ └── son.go
进入family目录并运行以下命令以创建一个名为 family 的go模块。
go mod init family
上面的命令将创建一个名为的文件。
要使用自定义包,我们必须先导入它。导入路径是附加在包的子目录和包名称上的模块名称。在我们的示例中,模块名称是 family,包父位于family正下方的父文件夹中。并且,包son位于父文件夹下的子文件夹中。
因此,"family/father"将导入father包,类似"family/father/son"将导入son包
package main
import (
parent "family/father"
child "family/father/son"
"fmt"
)
func main() {
f := new(parent.Father)
fmt.Println(f.Data("Mr. Jeremy Maclin"))
c := new(child.Son)
fmt.Println(c.Data("Riley Maclin"))
}
我们将father包别名为parent,将son别名为child。在main() 函数中,我们现在可以使用上面的别名。
在father文件夹中创建一个文件 。father内的文件应以package father开头,因为它属于father包。
# family\father\father.go
package father
import "fmt"
func init() {
fmt.Println("Father package initialized")
}
type Father struct {
Name string
}
func (f Father) Data(name string) string {
f.Name = "Father : " + name
return f.Name
}
init 函数可用于执行初始化工作,也可用于在执行开始之前确认程序的正确性。
在son文件夹中创建一个文件 。son文件夹中的文件应以package son开头,因为它属于 。
# family\father\son\son.go
package son
import "fmt"
func init() {
fmt.Println("Son package initialized")
}
type Son struct {
Name string
}
func (s Son) Data(name string) string {
s.Name = "Son : " + name
return s.Name
}
family>go run main.go
如果运行该程序,将获得以下输出。
Father package initialized
Son package initialized
Father : Mr. Jeremy Maclin
Son : Riley Maclin
结构是具有声明数据类型的数据字段的集合。Golang能够通过组合一个或多个类型(包括内置类型和用户定义类型)来声明和创建自己的数据类型。结构中的每个数据字段都使用已知类型声明,该类型可以是内置类型或其他用户定义类型。
结构是在Golang中创建具体的用户定义类型的唯一方法。结构类型是通过组合一组固定的唯一字段来声明的。结构可以提高模块化,并允许在系统周围创建和传递复杂的数据结构。您还可以将结构视为创建数据记录(如员工记录或电子商务产品)的模板。
声明以关键字type开头,然后是新结构的名称,最后是关键字struct。在大括号内,使用名称和类型指定了一系列数据字段。
type identifier struct{
field1 data_type
field2 data_type
field3 data_type
}
结构类型的声明
声明了一个结构类型矩形,该rectangle具有三个不同数据类型的数据字段。在这里,使用的结构不实例化该类型的新实例。
package main
import "fmt"
type rectangle struct {
length float64
breadth float64
color string
}
func main() {
fmt.Println(rectangle{10.5, 25.10, "red"})
}
rectangle结构及其字段不会导出到其他包,因为标识符以小写字母开头。在Golang中,如果名称以大写字母开头,则标识符将导出到其他包中,否则可访问性将仅在包内受到限制。
创建结构类型的实例
var关键字初始化变量rect。使用点表示法,将值分配给结构字段。
package main
import "fmt"
type rectangle struct {
length int
breadth int
color string
geometry struct {
area int
perimeter int
}
}
func main() {
var rect rectangle
rect.length = 10
rect.breadth = 20
rect.color = "Green"
rect.geometry.area = rect.length * rect.breadth
rect.geometry.perimeter = 2 * (rect.length + rect.breadth)
fmt.Println(rect)
fmt.Println("Area:\t", rect.geometry.area)
fmt.Println("Perimeter:", rect.geometry.perimeter)
}
结构将打印到终端,显示已分配值。
创建结构实例
通过使用结构文本并为结构的字段赋值来创建rectangle结构的实例。
package main
import "fmt"
type rectangle struct {
length int
breadth int
color string
}
func main() {
var rect1 = rectangle{10, 20, "Green"}
fmt.Println(rect1)
var rect2 = rectangle{length: 10, color: "Green"}
fmt.Println(rect2)
rect3 := rectangle{10, 20, "Green"}
fmt.Println(rect3)
rect4 := rectangle{length: 10, breadth: 20, color: "Green"}
fmt.Println(rect4)
rect5 := rectangle{breadth: 20, color: "Green"}
fmt.Println(rect5)
}
使用 new 关键字的结构实例化
还可以使用new关键字创建结构的实例。然后可以使用点表示法将数据值分配给数据字段。
package main
import "fmt"
type rectangle struct {
length int
breadth int
color string
}
func main() {
rect1 := new(rectangle) // rect1是指针
rect1.length = 10
rect1.breadth = 20
rect1.color = "Green"
fmt.Println(rect1)
var rect2 = new(rectangle) // rect2是实例对象
rect2.length = 10
rect2.color = "Red"
fmt.Println(rect2)
}
实例化矩形结构的两个实例,rect1 指向实例化结构的地址,rect2是它所表示的结构的名称。
使用指针地址运算符的结构实例化
使用指针地址创建rectangle结构的实例运算符&符号表示。
package main
import "fmt"
type rectangle struct {
length int
breadth int
color string
}
func main() {
var rect1 = &rectangle{10, 20, "Green"} // 不能省略任何属性值
fmt.Println(rect1)
var rect2 = &rectangle{}
rect2.length = 10
rect2.color = "Red"
fmt.Println(rect2)// breadth没有赋值
var rect3 = &rectangle{}
(*rect3).breadth = 10
(*rect3).color = "Blue"
fmt.Println(rect3)// length没有赋值
}
嵌套结构类型
可以通过使用其他结构类型作为结构字段的类型来创建结构类型来嵌套结构。将一个结构嵌套在另一个结构中可能是对更复杂的结构进行建模的有用方法。
package main
import "fmt"
type Salary struct {
Basic, HRA, TA float64
}
type Employee struct {
FirstName, LastName, Email string
Age int
MonthlySalary []Salary
}
func main() {
e := Employee{
FirstName: "Mark",
LastName: "Jones",
Email: "mark@gmail.com",
Age: 25,
MonthlySalary: []Salary{
Salary{
Basic: 15000.00,
HRA: 5000.00,
TA: 2000.00,
},
Salary{
Basic: 16000.00,
HRA: 5000.00,
TA: 2100.00,
},
Salary{
Basic: 17000.00,
HRA: 5000.00,
TA: 2200.00,
},
},
}
fmt.Println(e.FirstName, e.LastName)
fmt.Println(e.Age)
fmt.Println(e.Email)
fmt.Println(e.MonthlySalary[0])
fmt.Println(e.MonthlySalary[1])
fmt.Println(e.MonthlySalary[2])
}
在结构类型定义中使用字段标记
声明结构时,可以用``字段标记定义对应的字段字符串。
package main
import (
"fmt"
"encoding/json"
)
type Employee struct {
FirstName string `json:"firstname"`
LastName string `json:"lastname"`
City string `json:"city"`
}
func main() {
json_string := `
{
"firstname": "Rocky",
"lastname": "Sting",
"city": "London"
}`
emp1 := new(Employee)
json.Unmarshal([]byte(json_string), emp1)
fmt.Println(emp1)
emp2 := new(Employee)
emp2.FirstName = "Ramesh"
emp2.LastName = "Soni"
emp2.City = "Mumbai"
jsonStr, _ := json.Marshal(emp2)
fmt.Printf("%s\n", jsonStr)
}
标记表示为原始字符串值(包装在一对``中),并由正常代码执行忽略。
将方法添加到结构类型
还可以使用接收器将方法添加到结构类型。将方法EmpInfo添加到Employee结构中。
package main
import "fmt"
type Salary struct {
Basic, HRA, TA float64
}
type Employee struct {
FirstName, LastName, Email string
Age int
MonthlySalary []Salary
}
func (e Employee) EmpInfo() string {
fmt.Println(e.FirstName, e.LastName)
fmt.Println(e.Age)
fmt.Println(e.Email)
for _, info := range e.MonthlySalary {
fmt.Println("===================")
fmt.Println(info.Basic)
fmt.Println(info.HRA)
fmt.Println(info.TA)
}
return "----------------------"
}
func main() {
e := Employee{
FirstName: "Mark",
LastName: "Jones",
Email: "mark@gmail.com",
Age: 25,
MonthlySalary: []Salary{
Salary{
Basic: 15000.00,
HRA: 5000.00,
TA: 2000.00,
},
Salary{
Basic: 16000.00,
HRA: 5000.00,
TA: 2100.00,
},
Salary{
Basic: 17000.00,
HRA: 5000.00,
TA: 2200.00,
},
},
}
fmt.Println(e.EmpInfo())
}
为结构字段分配默认值
分配自定义默认值的方法可以通过使用构造函数来实现。Info函数可用于创建具有name和age字段的自定义默认值的结构,而不是直接创建结构。
package main
import "fmt"
type Employee struct {
Name string
Age int
}
func (obj *Employee) Info() {
if obj.Name == "" {
obj.Name = "John Doe"
}
if obj.Age == 0 {
obj.Age = 25
}
}
func main() {
emp1 := Employee{Name: "Mr. Fred"}
emp1.Info()
fmt.Println(emp1)
emp2 := Employee{Age: 26}
emp2.Info()
fmt.Println(emp2)
}
这是一种技术,而不是 Golang 规范的一部分。
查找结构类型
reflect包支持检查结构的基础类型。
package main
import (
"fmt"
"reflect"
)
type rectangle struct {
length float64
breadth float64
color string
}
func main() {
var rect1 = rectangle{10, 20, "Green"}
fmt.Println(reflect.TypeOf(rect1)) // main.rectangle
fmt.Println(reflect.ValueOf(rect1).Kind()) // struct
rect2 := rectangle{length: 10, breadth: 20, color: "Green"}
fmt.Println(reflect.TypeOf(rect2)) // main.rectangle
fmt.Println(reflect.ValueOf(rect2).Kind()) // struct
rect3 := new(rectangle)
fmt.Println(reflect.TypeOf(rect3)) // *main.rectangle
fmt.Println(reflect.ValueOf(rect3).Kind()) // ptr
var rect4 = &rectangle{}
fmt.Println(reflect.TypeOf(rect4)) // *main.rectangle
fmt.Println(reflect.ValueOf(rect4).Kind()) // ptr
}
将结构与分配给数据字段的不同值进行比较
可以使用比较运算符比较相同类型的结构。
package main
import "fmt"
type rectangle struct {
length float64
breadth float64
color string
}
func main() {
var rect1 = rectangle{10, 20, "Green"}
rect2 := rectangle{length: 20, breadth: 10, color: "Red"}
if rect1 == rect2 {
fmt.Println("True")
} else {
fmt.Println("False")
}
rect3 := new(rectangle)
var rect4 = &rectangle{}
if rect3 == rect4 {
fmt.Println("True")
} else {
fmt.Println("False")
}
}
使用值和指针引用复制结构类型
r2将与r1相同,它是R1的副本而不是对它的引用。 对r2所做的任何更改都不会应用于r1,反之亦然。更新r3时,分配给r1的基础内存也会更新。
package main
import "fmt"
type rectangle struct {
length float64
breadth float64
color string
}
func main() {
r1 := rectangle{10, 20, "Green"}
fmt.Println(r1)
r2 := r1
r2.color = "Pink"
fmt.Println(r2)
r3 := &r1
r3.color = "Red"
fmt.Println(r3)
fmt.Println(r1)
}
输出
{10 20 Green}
{10 20 Pink}
&{10 20 Red}
{10 20 Red}
由于r1和r3都引用相同的底层内存,因此它们的值是相同的。打印r3和r1的值显示值相同。