go语言结构体struct初始化N种方法(修改)
Go语言的结构体是一种自定义的复合数据类型,允许将不同类型的数据字段组合成一个整体,用于封装相关数据,类似于其他语言中的类,可以定义字段和方法。通过type TypeName struct { ... }来定义,使用 .(dot) 操作符访问字段,支持零值初始化、指针访问、结构体嵌套或组合和interface结合定义方法,也可以自己定义方法,是实现Go面向对象编程(OOP)的核心机制之一。你可以在go源码中大量看到struct和interface结合使用、struct单独使用、interface单独使用、func等的应用。特别是struct出现频率非常高。
一、Stringer接口
type UserTest struct { Name string address string age int money float64 data []byte } /* // Stringer接口被定义在fmt中,会调用,下面是他的一段解释 // Stringer is implemented by any value that has a String method, // which defines the ``native'' format for that value. // The String method is used to print values passed as an operand // to any format that accepts a string or to an unformatted printer // such as Print. type Stringer interface { String() string } */ func (u UserTest) String() string { return fmt.Sprintf("%s is %d %v", u.Name, u.age, u.data) } func main(){ u1:= &UserTest{ name: "ao",age: "34"} fmt.Println(u1) //输出: ao is 34 [1 2 3] }
二、类型断言
/* 允许提取接口的底层类型, CType 表示具体类型 value := interfaceVariable.(CType) 这里 DStr 自动实现了Si接口的方法 */ type Si interface { //定义两个方法 Si_method() float64 p_method() bool } //DStr实现Si接口方法 type DStr struct { it float64 } func (s DStr) Si_method() float64 { return s.it * s.it } func (s DStr) p_method() bool { return false } //调用方法 func callAmethod(s interface{}) { value, ok := s.(Si) if ok { fmt.Println("Si_method:", value.Si_method()) } else { fmt.Println("不是 Si 接口") } } func main() { ds := DStr{it: 4} callAmethod(ds) }
三、interface结合struct
// Define the interface type Iinterface interface { smethod() float64 //定义方法 } // Cs type 实现 Iinterface 接口 type Cs struct { csfield float64 } //实现interface smethod方法 func (c Cs) smethod() float64 { return c.csfield * c.csfield } /* 调用 接口可以保留任意值,但实际值及其类型是动态存储的 func main(){ var i Iinterface fmt.Println("Value of i:", i) // nil } */ //调用2 func main(){ var i Iinterface i = Cs{csfield: 5} fmt.Println("C smethod:", s.smethod()) }
四、结构体数组
type user struct { name string age uint64 } func userfunc(){ u := []user{ {"huang",23}, {"li",19}, {"zhang",18}, } //使用'_' ,则会忽略key和索引值 for _,v := range u{ if v.age != 18{ v.age = 20 } //格式化 format := "%-6v %3v\n" fmt.Printf(format, "data:", v.age) } //fmt.Println(u) }
五、结构体数组
func 遍历struct(){ type cat struct { Name string // 带有结构体tag的字段 Type int `json:"type" id:"100"` } // 创建cat的实例 ins := cat{Name: "mimi", Type: 1} // 获取结构体实例的反射类型对象 typeOfCat := reflect.TypeOf(ins) // 遍历结构体所有成员 for i := 0; i < typeOfCat.NumField(); i++ { // 获取每个成员的结构体字段类型 fieldType := typeOfCat.Field(i) // 输出成员名和tag fmt.Printf("name: %v tag: '%v'\n", fieldType.Name, fieldType.Tag) } // 通过字段名,找到字段类型信息 if catType, ok := typeOfCat.FieldByName("Type"); ok { // 从tag中取出需要的tag fmt.Println(catType.Tag.Get("json"), catType.Tag.Get("id")) } fmt.Println("\n") }
六、结构体内用函数作为字段
//来自: C:\Go\src\time\sleep.go type functiontype func(string) string type Access struct { name string f functiontype //声明一个函数 } func main(){ upr := Access{ name: "orange", f: func(name string) string { return name }, } fmt.Println(upr.f(upr.name)) }
七、我们可以从一个结构体访问另一个匿名结构体
//请参考: C:\Go\src\time\tick.go Ticker结构体,runtimeTimer结构体定义在sleep.go type Tss struct{ name string } type Ms struct{ Tss }
func main(){
//从一个结构体访问另一个结构体
var p = Ms{}
p.Tss = Tss{"aozhejin"}
}
八、使用结构体数组
//使用结构体数组
//使用于一个结构体数组
type ArrayS struct {
name string
}
func main(){
var ArraySs []ArrayS as:= ArrayS{"li"} k := append(ArraySs, as) fmt.Println(k)
}
主测试
type BufferG struct { buf []byte offset int lastRead string } func (p *BufferG) IsValid() bool { return p.offset > 1 } func (p *BufferG) empty() bool { return len(p.buf) <= p.offset } //返回2个参数 func (p *BufferG) twoparam(n int) (int, bool) { // return 0, false } //返回1个参数,并且注意并没有使用 *BufferG,而是BufferG,Go 会自动处理值之间的转换以及方法调用的指针。 //你可能想使用指针接收类型以避免在方法调用中复制调用,或者允许方法变异接收结构体 func (p BufferG) spaceSplit(n int) int { // return 0 } //定义函数,返回BufferG func newBufferG(s string) *BufferG { p := BufferG{lastRead: s} p.offset = 4 return &p } //定义另外一个方法,返回*BufferG func (p *BufferG)newBufferG2(s string) *BufferG { //Anonymous struct(匿名结构体不能在函数、方法、main之外) A_Str := struct { name string isGood bool }{ "go", true, } fmt.Println(A_Str) //顺序和参数个数和BufferG结构体定义都可不匹配 p = &BufferG{lastRead: s} p.offset = 44 return p } func (p BufferG)defaultBufferG() *BufferG { // //如果想初始化,并且使用 &方式,则返回变量为*BufferG,否则会报错 return &BufferG{buf:[]byte{1,2,3},offset: 4, lastRead:"default value"} }
func main() {
//实例化方式1,使用new关键字
//new(T)形式和&T{}形式等同,new函数分配内存并返回指向该结构体内存的指针
f:=new(BufferG)
fmt.Println(&f) //f是一个指针类型的对象,f存放的是BufferG的内存地址 ,用&f就可以取得放在f里面的内存地址 0xc000006030
f.lastRead = "address content"
//我们想取得f指针指向的内存地址上的数据,怎么写呢?
fmt.Printf("per指针对象指向的内存地址lastRead的内容为: %s \n", (*f).lastRead)
//test
var _ string =f.lastRead
//实例化方式2
var _ interface{}= &BufferG{}
//实例化3,初始化时,参数个数可以不匹配(键值对形式初始化)
s := BufferG{lastRead: "aozhejin", offset: 5}
fmt.Println(s.lastRead)
//引用
sp := &s
fmt.Println(sp.offset)
sp.offset = 51
fmt.Println(sp.offset)
//实例化4, 注意实例之间并无关系
//声明 BufferG 结构体实例 不进行初始化
var buf BufferG
if buf.IsValid() {
fmt.Println("条件成立")
}
//此数据offset=0,并不会是上面设置的51
fmt.Println(buf.offset)
if buf.offset==51{
panic("错误")
}
//var关键字来声明
var buf2 =BufferG{}
fmt.Println(buf2.offset)
//实例化5,使用指针地址操作
//&T{var...} 是一种简写,底层仍会调用 new(),变量声明采用:=
//表达式 new(T) 和 &T{} 形式是等价的
//这种形式,字段初始化和结构体定义的字段顺序无关,同时也和是否设置了某字段初始值无关,我们这里只初始化了2个参数
//顺序和参数个数都不匹配
b:= &BufferG{ offset: 4,lastRead: "cheng"}
b.offset=3
fmt.Println(b) //&{[] 3 cheng}
//忽略twoparam方法的第二个参数
c,_:=b.twoparam(0)
fmt.Println(c)
//实例化6
//参数个数匹配,但是可以没有顺序关系
six:=BufferG{lastRead: "six", offset: 3,buf:[]byte("ex")}
fmt.Println(six)
//{[101 120] 3 six} ,输出的内容是按照BufferG定义的进行输出
//参数个数可以不匹配
fmt.Println(BufferG{lastRead: "lastread"})
//{[] 0 lastread} , 由于其他两个字段没有设置,所以位默认值
//实例7 struct literal syntax
//第1种形式,参数个数必须匹配,初始化失败
//fmt.Println(BufferG{"ao", 20}) //报错太少的值,参数不匹配
//第2种形式,字段名必须在struct当中定义的,
//fmt.Println(BufferG{names: "jin"})
//注意参数的数据类型也要匹配
fmt.Println(BufferG{[]byte("aozhejin"),5, "go"})
//{[97 111 122 104 101 106 105 110] 5 go}
//实例化8(实际使用的是实例化3形式)
fmt.Println(newBufferG("newpersongo"))
//&{[] 4 newpersongo}
//实例化9,defaultBufferG和newBufferG类似
//隐式结构体声明(Anonymous struct),这种不能声明的体外(不能在func、main、方法等外)
dog := struct {
name string
isGood bool
}{
"go",
true,
}
fmt.Println(dog) //输出: {go true}
}
九、结构体定义方法
//注意同一个包中的结构体名称不能为一样的 // 定义主结构体 type Person struct { name string age int //money float32 //address string } //给Person定义一个方法 func (p Person) PrintName(){ fmt.Println(p.name) } //给Person定义一个方法ChangeNameTest func (p Person) ChangeNameTest(str string){ p.name = str } //引用传值 func (p *Person) GotoShoping(str string){ fmt.Println(p.name) } func main(){ // 初始化一个Person类型体 p := Person{"aozhejin",32} //调用ChangeNameTest方法 p.ChangeNameTest("zhui") // 值类型不能修改接收者 p.PrintName() // 输出仍然为"zhui" // y为引用类型,创建指针类型y y := &Person{"aozhejin1",34} y.ChangeNameTest("zhui2") // 运行(*y).ChangeName("zhui2"), 由于是值传递进去的,因此不能修改接收者 y.PrintName() // 输出仍为"aozhejin1" //*main.Person getP := reflect.TypeOf(y) fmt.Println(getP) }
十、典型应用
1.下面样例内容来自: C:\Go\src\bufio\example_test.go
https://gobyexample.com/structs https://golangdocs.com/structs-in-golang
package main import ( "fmt" "bufio" "os" "strings" ) func calltest(){ s:=strings.ToUpper("hello") fmt.Println(s) scanner := bufio.NewScanner(strings.NewReader("gopher")) for scanner.Scan() { fmt.Println(len(scanner.Bytes()) == 6) } if err := scanner.Err(); err != nil { fmt.Fprintln(os.Stderr, "shouldn't see an error scanning a string") } }
C:\Go\src\bufio\scan.go源码
type Scanner struct { r io.Reader // The reader provided by the client. split SplitFunc // The function to split the tokens. maxTokenSize int // Maximum size of a token; modified by tests. token []byte // Last token returned by split. buf []byte // Buffer used as argument to split. start int // First non-processed byte in buf. end int // End of data in buf. err error // Sticky error. empties int // Count of successive empty tokens. scanCalled bool // Scan has been called; buffer is in use. done bool // Scan has finished. } func NewScanner(r io.Reader) *Scanner {return &Scanner{r:r,split:ScanLines,maxTokenSize: MaxScanTokenSize,}}
2. 典型应用演示的主要是io.go在外部调用的一些比较明显的特征以及内部的一些逻辑处理
例子来自: C:\Go\src\io\example_test.go
package main import ( "io" "os" "strings" ) //三种不同的读取方式 func ExampleLimitReader() { r := strings.NewReader("io.Reader LimitReader to be read\n") lr := io.LimitReader(r, 4) if _, err := io.Copy(os.Stdout, lr); err != nil { log.Fatal(err) } } func ExampleTeeReader() { var r io.Reader = strings.NewReader("io.Reader TeeReader to be read\n") r = io.TeeReader(r, os.Stdout) // ReadAll方法 调用的是 TeeReader's Reader方法 io.ReadAll(r) // Output: // some io.Reader stream to be read } func ExampleSectionReader() { r := strings.NewReader("io.Reader SectionReader to be read\n") s := io.NewSectionReader(r, 5, 17) if _, err := io.Copy(os.Stdout, s); err != nil { log.Fatal(err) } } func main(){ ExampleTeeReader() ExampleLimitReader() ExampleSectionReader() }
源码分析: C:\Go\src\io\io.go
/* //外部调用,大写 , Reader、Writer即io.Reader,io.Writer func TeeReader(r Reader, w Writer) Reader { return &teeReader{r, w} } //内部调用小写,名称一样 type teeReader struct { r Reader w Writer } //定义teeReader的Read方法 func (t *teeReader) Read(p []byte) (n int, err error) { n, err = t.r.Read(p) if n > 0 { if n, err := t.w.Write(p[:n]); err != nil { return n, err } } return } //外部调用大写 func NewSectionReader(r ReaderAt, off int64, n int64) *SectionReader { return &SectionReader{r, off, off, off + n} } //这个虽然不标准,内部没有小写 type SectionReader struct { r ReaderAt base int64 off int64 limit int64 } //定义SectionReader结构体的read方法 func (s *SectionReader) Read(p []byte) (n int, err error) { if s.off >= s.limit { return 0, EOF } if max := s.limit - s.off; int64(len(p)) > max { p = p[0:max] } n, err = s.r.ReadAt(p, s.off) s.off += int64(n) return } func ReadAll(r Reader) ([]byte, error) { b := make([]byte, 0, 512) for { if len(b) == cap(b) { // Add more capacity (let append pick how much). b = append(b, 0)[:len(b)] } n, err := r.Read(b[len(b):cap(b)]) b = b[:len(b)+n] if err != nil { if err == EOF { err = nil } return b, err } } } */

浙公网安备 33010602011771号