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
        }
    }
}
*/

 

 

 

posted @ 2023-02-19 23:43  jinzi  阅读(101)  评论(0)    收藏  举报