Go语言类型的本质

如果给这个类型增加或者删除某个值,是要创建一个新值,还是要更改当前的值?

如果是要创建一个新值,该类型的方法就使用值接收者。

如果是要修改当前值,就使用指针接收者。

这个答案也会影响程序内部传递这个类型的值的方式:是按值做传递,还是按指针做传递。

保持传递的一致性很重要。

 

一、内置类型

字符串(string)就像整数、浮点数和布尔值一样,本质上是一种很原始的数据值,所以在函数或方法内外传递时,要传递字符串的一份副本。

 

二、引用类型

切片、映射、通道、接口和函数类型等。当声明上述类型的变量时,创建的变量被称作标头(header)值。字符串也是一种引用类型。

 

标头值:是包含一个指向底层数据结构的指针,所以通过复制来传递一个引用类型的值的副本,本质上就是在共享底层数据结构。

 

三、结构类型

结构类型可以用来描述一组数据值,这组值的本质即可以是原始的,也可以是非原始的。

有两个例子提供:

type Time struct {
    // sec 给出自公元1 年1 月1 日00:00:00
    // 开始的秒数
    sec int64    
    // nsec 指定了一秒内的纳秒偏移,
    // 这个值是非零值,
    // 必须在[0, 999999999]范围内
    nsec int32   
    // loc 指定了一个Location,
    // 用于决定该时间对应的当地的分、小时、
    // 天和年的值
    // 只有Time 的零值,其loc 的值是nil
    // 这种情况下,认为处于UTC 时区
    loc *Location
}

Now方法

func Now() Time {
    sec, nsec := now()
    return Time{sec + unixToInternal, nsec, Local}
}

Time对象的方法

func (t Time) Add(d Duration) Time {
     t.sec += int64(d / 1e9)
     nsec := int32(t.nsec) + int32(d%1e9)
     if nsec >= 1e9 {
     t.sec++
     nsec -= 1e9
     } else if nsec < 0 {
     t.sec--
     nsec += 1e9
     }
     t.nsec = nsec
}

Now方法和Time对象的方法,可以看到传入和参数和返回的值全是值传递的。也就是说返回的都是一个值的副本。

 

有如下的结构体,为什么File中只有一个*file,这个目前未知,这样设计的好处?

// File 表示一个打开的文件描述符
type File struct {
    *file
}

// file 是*File 的实际表示
// 额外的一层结构保证没有哪个os 的客户端
// 能够覆盖这些数据。如果覆盖这些数据,
// 可能在变量终结时关闭错误的文件描述符
type file struct {
   fd int
   name string
   dirinfo *dirInfo // 除了目录结构,此字段为nil
   nepipe int32 // Write 操作时遇到连续EPIPE 的次数
}

open方法,参数传入了File的指针

func Open(name string) (file *File, err error) {
     return OpenFile(name, O_RDONLY, 0)
}

File对象的方法,是引用传递的

func (f *File) Chdir() error {
    if f == nil {
        return ErrInvalid
    }
    if e := syscall.Fchdir(f.fd); e != nil {
        return &PathError{"chdir", f.name, e}
    }
    return nil
}

为什么用引用,因为File 类型的值具备非原始的本质,所以总是应该被共享,而不是被复制。

这一章节主要想说明什么呢?主要是想说,在设计一个方法的参数、返回值、接收者的时候,需要考虑类型本身是什么东西,例如值类型,这个本身就是传值的复制类型,而引用类型的本身就是传递地址的。对于结构类型需要看类型本身是需要被改写的还是一个副本、

 

原文地址:https://www.cnblogs.com/dcz2015/p/10103254.html