随笔 - 231  文章 - 205  评论 - 20  阅读 - 41万

Go新手容易踩的坑(函数与方法)

回到顶部

方法的接收器 —— 对象接收器与指针接收器

对象接收器不会更新属性

复制代码
package tests

import (
    "fmt"
    "testing"
)

type Consumer struct {
    Balance int64
}

// 对象接收器
func (c Consumer) add(v int64) {
    c.Balance += v
}

func TestT1(t *testing.T) {

    c := Consumer{Balance: 10}
    
    // 不会改变...
    c.add(100)

    fmt.Println("c.Balance:>> ", c.Balance) // 10
}
复制代码

指针接收器可以更新属性

复制代码
package tests

import (
    "fmt"
    "testing"
)

type Consumer struct {
    Balance int64
}

// 指针接收器
func (c *Consumer) add(v int64) {
    c.Balance += v
}

func TestT1(t *testing.T) {

    c := Consumer{Balance: 10}
    
    // 会改变...
    c.add(100)

    fmt.Println("c.Balance:>> ", c.Balance) // 110
}
复制代码
回到顶部

返回一个nil接收器(注意这个接收器实际上是有类型的)

复制代码
package tests

import (
    "errors"
    "fmt"
    "strings"
    "testing"
)

type Consumer struct {
    Name string
    Age  int
}

type MultiError struct {
    errs []string
}

func (m *MultiError) add(err error) {
    m.errs = append(m.errs, err.Error())
}

// 实现 error 接口
func (m *MultiError) Error() string {
    return strings.Join(m.errs, ";")
}

func (c *Consumer) Validate() error {
    var m *MultiError

    if c.Name == "" {
        m = &MultiError{}
        m.add(errors.New("名字是空!"))
    }

    if c.Age == 0 {
        if m == nil {
            m = &MultiError{}
        }
        m.add(errors.New("年龄是0!"))
    }

    return m
}

func TestT1(t *testing.T) {

    c := Consumer{Name: "naruto", Age: 10}

    if err := c.Validate(); err != nil {
        // Notice 原因是:这个err有类型!(go里面一个变量有2个属性:type与value,虽然value是nil,但是它的type是*MultiError)
        fmt.Println("发生了err: ", err) // 发生了err:  <nil>
    }
}
复制代码
回到顶部

忽略defer语句参数和接收器的计算1 —— 参数计算

有问题的代码

复制代码
package tests

import (
    "errors"
    "fmt"
    "testing"
)

const (
    StatusSuccess  = "success"
    StatusErrorFoo = "error_foo"
    StatusErrorBar = "error_bar"
)

func notify(s string) {
    fmt.Println("执行了notify, s: ", s)
}

func incrementCounter(s string) {
    fmt.Println("执行了incrementCounter, s: ", s)
}

func foo() error {

    //return nil
    return errors.New("foo的报错...")

}

func f() error {
    var status string

    // Notice status参数是立即被计算的,所以这两个方法中的status参数是空字符串!!!
    defer notify(status)
    defer incrementCounter(status)

    if err := foo(); err != nil {
        status = StatusErrorFoo
        return err
    }

    status = StatusSuccess
    return nil
}

func TestT1(t *testing.T) {

    f()
    // // 打印了空字符串!!!
    /*
        执行了incrementCounter, s:
        执行了notify, s:
    */

}
复制代码

修改方案1:使用指针

复制代码
package tests

import (
    "errors"
    "fmt"
    "testing"
)

const (
    StatusSuccess  = "success"
    StatusErrorFoo = "error_foo"
    StatusErrorBar = "error_bar"
)

func notify(s *string) {
    fmt.Println("执行了notify, s: ", *s)
}

func incrementCounter(s *string) {
    fmt.Println("执行了incrementCounter, s: ", *s)
}

func foo() error {

    //return nil
    return errors.New("foo的报错...")

}

func f() error {
    var status string

    // Notice 传 字符串指针
    defer notify(&status)
    defer incrementCounter(&status)

    if err := foo(); err != nil {
        status = StatusErrorFoo
        return err
    }

    status = StatusSuccess
    return nil
}

func TestT1(t *testing.T) {

    f()
    /*
        执行了incrementCounter, s:  error_foo
        执行了notify, s:  error_foo
    */

}
复制代码

修改方案2:将闭包作为defer语句调用

复制代码
package tests

import (
    "errors"
    "fmt"
    "testing"
)

const (
    StatusSuccess  = "success"
    StatusErrorFoo = "error_foo"
    StatusErrorBar = "error_bar"
)

func notify(s string) {
    fmt.Println("执行了notify, s: ", s)
}

func incrementCounter(s string) {
    fmt.Println("执行了incrementCounter, s: ", s)
}

func foo() error {

    //return nil
    return errors.New("foo的报错...")

}

func f() error {
    var status string

    // Notice 将闭包作为defer语句调用
    // 闭包:对外部环境变量引用的函数
    defer func() {
        notify(status)
        incrementCounter(status)
    }()

    if err := foo(); err != nil {
        status = StatusErrorFoo
        return err
    }

    status = StatusSuccess
    return nil
}

func TestT1(t *testing.T) {

    f()
    /*
        执行了incrementCounter, s:  error_foo
        执行了notify, s:  error_foo
    */

}
复制代码
回到顶部

忽略defer语句参数和接收器的计算2 —— 指针和值接收器

值接收器的情况

复制代码
package tests

import (
    "fmt"
    "testing"
)

type Consumer struct {
    Name string
}

// 值接收器
func (c Consumer) printName() {
    fmt.Println("name: ", c.Name)
}

func TestT1(t *testing.T) {

    c := Consumer{Name: "foo"}
    // defer 立即获取当前的值...
    defer c.printName() // foo

    c.Name = "bar"

}
复制代码

指针接收器的情况

复制代码
package tests

import (
    "fmt"
    "testing"
)

type Consumer struct {
    Name string
}

// 值接收器
func (c *Consumer) printName() {
    fmt.Println("name: ", c.Name)
}

func TestT1(t *testing.T) {

    c := Consumer{Name: "foo"}
    // defer 注意这里是指针...
    defer c.printName() // bar

    c.Name = "bar"

}
复制代码

~~~

posted on   江湖乄夜雨  阅读(21)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
历史上的今天:
2022-07-14 Python实现AES加解密以及发送加解密请求
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示