golang 结构体

普通结构体

1.public

type Flower struct {
    Name string
    Color string
    Category string
}

外部包可以直接引用和赋值
2.private

type leaf struct {
    count int
    size int
}

外部包不能直接引用
需要定义public函数提供操作,例如

func (l *leaf) SetColor(color string) {
    l.color = color
}

3.public结构体里面有private

type Flower struct {
    Name string
    Color string
    Category string
}

这种情况下,只能通过public的函数或者方法来操作内部的private属性
4.private结构体里面有public属性

type leaf struct {
    color string
    size int
    Count int
}

这种也是允许的,例如:

func NewLeaf(color string, size, count int) *leaf{
    var l = leaf{color, size, count}
    return &l
}

在另一个包中引用这些结构体

func main() {
    leaf := plant.NewLeaf("green", 5, 10)
    fmt.Println(leaf)
    leaf.Count = 5
    fmt.Println(leaf)
}

输出:

&{green 5 10}
&{green 5 5}

很少看到这么使用

嵌套结构体

type Leaf struct {
    Color string
    Size int
    Count int
}

type Flower struct {
    Leaf Leaf
    Name string
    Color string
    Category string
}

访问方式:

func main() {
    f := plant.Flower{}
    f.Category = "Roseceae"
    f.Name = "Rose"
    f.Color = "Red"
    f.Leaf.Color = "green"
    f.Leaf.Count = 5
    f.Leaf.Size = 10

    fmt.Println(f)
}

这种嵌套,在访问内部结构的成员时,必须显示的指向,如:
f.Leaf.Color

嵌套匿名结构体

type Leaf struct {
    Color string
    Size int
    Count int
}

type Flower struct {
    Leaf
    Name string
    Color string
    Category string
}

访问方式:

func main() {
    f := plant.Flower{}
    f.Category = "Roseceae"
    f.Name = "Rose"
    f.Color = "Red"
    f.Count = 5
    f.Size = 10
    f.Leaf.Color = "green"

    fmt.Println(f)
}

如上可以看出,有两种访问方式:
a.直接由外部结构体访问内部结构体成员,跟访问外部结构体内部自己的成员一样

f.Count = 5
f.Size = 10

b.外部结构体指向内部结构体,再访问其内部成员

f.Leaf.Color = "green"

注意:在内外结构体有成员名称相同时,如果访问嵌套结构体的成员,必须按照b方式访问,否则是直接访问的外层结构体成员变量。

结构体中嵌套接口

以golang内置的包context为例

type Context interface {
    Deadline() (deadline time.Time, ok bool)
    Done() <-chan struct{}
    Err() error
    Value(key interface{}) interface{}
}

// A canceler is a context type that can be canceled directly. The
// implementations are *cancelCtx and *timerCtx.
type canceler interface {
    cancel(removeFromParent bool, err error)
    Done() <-chan struct{}
}

// A cancelCtx can be canceled. When canceled, it also cancels any children
// that implement canceler.
type cancelCtx struct {
    Context

    mu       sync.Mutex            // protects following fields
    done     chan struct{}         // created lazily, closed by first cancel call
    children map[canceler]struct{} // set to nil by the first cancel call
    err      error                 // set to non-nil by the first cancel call
}

这种嵌套,可以当成继承来理解,
首先Context接口有四个方法,全部被cancelCtx这个结构体继承了,然后重写了其中三个方法

func (c *cancelCtx) Value(key interface{}) interface{} {
    if key == &cancelCtxKey {
        return c
    }
    return c.Context.Value(key)
}

func (c *cancelCtx) Done() <-chan struct{} {
    c.mu.Lock()
    if c.done == nil {
        c.done = make(chan struct{})
    }
    d := c.done
    c.mu.Unlock()
    return d
}

func (c *cancelCtx) Err() error {
    c.mu.Lock()
    err := c.err
    c.mu.Unlock()
    return err
}

同时,cancelCtx又实现了canceler这个接口定义的方法,所以cancelCtx又可以当成canceler接口用:


// cancel closes c.done, cancels each of c's children, and, if
// removeFromParent is true, removes c from its parent's children.
func (c *cancelCtx) cancel(removeFromParent bool, err error) {
    //具体实现见源码
}

func WithCancel(parent Context) (ctx Context, cancel CancelFunc) {
    c := newCancelCtx(parent)
    propagateCancel(parent, &c)
    return &c, func() { c.cancel(true, Canceled) }
}

// newCancelCtx returns an initialized cancelCtx.
func newCancelCtx(parent Context) cancelCtx {
    return cancelCtx{Context: parent}
}


// propagateCancel arranges for child to be canceled when parent is.
func propagateCancel(parent Context, child canceler) {
    //具体实现见源码
}

从WithCancel中可以看到,执行c := newCancelCtx(parent)之后,c其实是cancelCtx类型,再调用propagateCancel时,第二个参数是canceler接口类型,直接取c地址传入,然后&c就当canceler接口用了,最后return时,又将&c当Context接口返回了

Context包代码不多,可以自己研究源码,认识会更深刻。

json/xml结构体

JSON:
Json结构体定义的时候,成员名称和结构体名称必须可导出,即必须以大写开头,如:

type Os struct {
    Arch     string
    Major    string
    Minor    string
    Name     string
    Platform string
    Version  string
}

但是,这样在序列化成json字符串的时候,会以成员名称作为json字符串中的名称:

func main() {
    linux := Os{
        Arch: "x86_64",
        Major: "7",
        Minor: "7",
        Name: "CentOS Linux",
        Version: "7.7",
    }
    js, err := json.Marshal(linux)
    if err != nil {
        return
    }
    fmt.Println(string(js))
}

输出:

{"Arch":"x86_64","Major":"7","Minor":"7","Name":"CentOS Linux","Platform":"","Version":"7.7"}

有些情况下,我们的json字符串想全部以小写开头,则可以用如下方法指定输出时的字符串名称:

type Os struct {
    Arch     string `json:"arch"`
    Major    string `json:"major"`
    Minor    string `json:"minor"`
    Name     string `json:"name"`
    Platform string `json:"platform"`
    Version  string `json:"version"`
}

可以直接用内置的接口进行序列化处理:

func main() {
    linux := Os{
        Arch: "x86_64",
        Major: "7",
        Minor: "7",
        Name: "CentOS Linux",
        Platform: "centos",
        Version: "7.7",
    }
    js, err := json.Marshal(linux)
    if err != nil {
        return
    }
    fmt.Println(string(js))
}

输出:

{"arch":"x86_64","major":"7","minor":"7","name":"CentOS Linux","platform":"centos","version":"7.7"}

如果某个字段没有赋值,例如Platform:

func main() {
    linux := Os{
        Arch: "x86_64",
        Major: "7",
        Minor: "7",
        Name: "CentOS Linux",
        Version: "7.7",
    }
    js, err := json.Marshal(linux)
    if err != nil {
        return
    }
    fmt.Println(string(js))
}

输出:

{"arch":"x86_64","major":"7","minor":"7","name":"CentOS Linux","platform":"","version":"7.7"}

可以看到json字符串中仍然生成了platform这个字段,如果想对于某些不赋值的字段在生成json串的时候忽略掉,则可以在定义结构体的时候,使用omitempty,定义如下:

type Os struct {
    Arch     string `json:"arch"`
    Major    string `json:"major"`
    Minor    string `json:"minor"`
    Name     string `json:"name"`
    Platform string `json:"platform,omitempty"`
    Version  string `json:"version"`
}

func main() {
    linux := Os{
        Arch: "x86_64",
        Major: "7",
        Minor: "7",
        Name: "CentOS Linux",
        Version: "7.7",
    }
    js, err := json.Marshal(linux)
    if err != nil {
        return
    }
    fmt.Println(string(js))
}

输出:

{"arch":"x86_64","major":"7","minor":"7","name":"CentOS Linux","version":"7.7"}

注意注意注意:

`json:"platform,omitempty"`

这个字符串中不能包含空格,否则无效
例如结构体中定义为:

Platform string `json: "platform,omitempty"`

这种json:后面跟空格,则不能指定json序列化时候显示的成员名称,默认是结构体成员名,即Platform

{"arch":"x86_64","major":"7","minor":"7","name":"CentOS Linux","Platform":"","version":"7.7"}

发表评论

电子邮件地址不会被公开。 必填项已用*标注