我来自C#背景,对于Go语言的初始化和零值定义方式感到有些困惑。我想你可以猜到这种混淆是由Go中的make()
和new()
函数引起的。当这些方法运行时,我应该期望发生什么内部操作?初始化和零值操作会发生什么情况?
我知道在Go语言中有一个init()
函数用于初始化包,但我认为这与此不同。
无论如何,它们之间有什么区别?
更新
我回答了自己的问题,请查看我的答案。
我来自C#背景,对于Go语言的初始化和零值定义方式感到有些困惑。我想你可以猜到这种混淆是由Go中的make()
和new()
函数引起的。当这些方法运行时,我应该期望发生什么内部操作?初始化和零值操作会发生什么情况?
我知道在Go语言中有一个init()
函数用于初始化包,但我认为这与此不同。
无论如何,它们之间有什么区别?
我回答了自己的问题,请查看我的答案。
我认为我已经理解了它,并决定分享一下我到目前为止所理解的内容。
make()
vs. new()
我现在明白了make()
和new()
之间的区别。起初,有点困惑,但我现在明白了:
new
就像C#或Java中的new
,但由于Go语言中没有构造函数,所有字段(如Java和C#术语中的字段)都将被清零。清零更像是将字段默认为零。因此,如果字段类型是int
,那么它将是0
,如果是struct
,则将默认为nil
,对于string
类型,则为""
。实际上,当只有无参构造函数可用且您未手动设置成员时,它与C#和Java类似。
但是,像map
、slice
和channel
这样的类型是不同的。它们是不同的,因为它们实际上是包装器类型,它们包装了一个数组类型以在幕后保存值。因此,类似于C#和Java中的List<T>
或ArrayList
。但在这种情况下,仅使用new
是不够的,因为底层数组应该被初始化为一个空数组才能使用。因为您无法向类型为nil
(或null
)的数组类型的字段添加或删除内容。因此,他们提供了make()
方法来帮助您初始化slices
等。
那么,当您在片段中使用new()
时会发生什么?简单:由于底层数组将是nil
,因此片段将指向一个nil
数组。
因此,new()
看起来就像以下C#/Java代码:
public class Person{
public string Name;
public int Age;
public Address HomeAddress;
}
var person = new Person();
Console.WriteLine(person.Name); // ""
Console.WriteLine(person.Age); // 0
Console.WriteLine(person.HomeAddress); // null
make()
对于slice
、map
和 channel
则会像这样:
public class PersonList{
// We are initializing the array so that we can use it.
// Its capacity can increase.
private Person[] _personList = new Person[100];
public void Add(Person p){}
public void Remove(Person p){}
public Person Get(int index){}
}
简单来说,清零是一种初始化形式。起初,我认为它们不同,但实际上它们并不是。初始化是一个更通用的术语,而如果你将结构体或变量的字段(属性等)设置为其类型的默认值,如0
、nil
、""
、false
等,则称之为清零。然而,你可以使用组合字面量,比如hello := Hello{name="world"}
,这类似于C#中的var hello = new Hello() {Name = "World"}
,然后你就可以将你的Hello
对象初始化为一个name
字段设置为world
。
在C#中,当你说new List<string>()
时,[底层数组字段会初始化为新数组],而make()
则在幕后执行了类似的操作,但作为一种语言构造(内置于语言本身):
(http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,cf7f4095e4de7646):
因此,new
会进行清零并返回一个指针。而make()
则将底层数组初始化为每个元素的默认值,并返回值本身而不是指针。
new
返回一个指向类型的指针,这是一次返回本地类型指针的唯一方式 (比如 int、float、complex):
intPtr := new(int)
// or
var i int
intPtr := &i
make
用于初始化通道、切片和映射。make
的规范: https://golang.org/ref/spec#Making_slices_maps_and_channels
有关零值的规范: https://golang.org/ref/spec#The_zero_value
&type{}
。你不能在结构体上使用 make。 - OneOfOnenew(T)
分配类型为 T 的新项的零值存储,并返回其地址,即类型为 *T 的值:它返回指向新分配的类型为 T 的零值的指针,可以直接使用;它适用于数组和结构等值类型。等同于 &T{ }
make(T)
返回类型为 T 的已初始化值;仅适用于三种内置引用类型:切片、映射和通道。
var p *[]int = new([]int) // *p == nil; with len and cap 0
//or
p := new([]int)
var v []int = make([]int, 10, 50)
//or
v := make([]int, 10, 50)
//This allocates an array of 50 ints and then creates a slice v with length 10 and capacity 50 pointing
//to the first 10 elements of the array
var t Type
被清零,t := &Type{}
或var t Type; ptr := &t
与t := new(Type)
相同,但在 new 返回指针之前,该类型会被清零。 - OneOfOne