Heim  >  Artikel  >  Backend-Entwicklung  >  Analysieren Sie den Typvergleich in go

Analysieren Sie den Typvergleich in go

藏色散人
藏色散人nach vorne
2021-06-21 16:09:533295Durchsuche

Die folgende Tutorial-Kolumne von golang stellt Ihnen die Analyse des Typvergleichs in Go vor. Ich hoffe, dass sie Freunden in Not hilfreich sein wird!

Übersicht

In einem kürzlich durchgeführten Interview fragte der Interviewer nach dem Typvergleich zwischen go. Grundsätzlich ist die Grundlage nicht stark genug! Ich habe eine Menge Informationen im Internet gelesen und selbst ein paar einfache Zusammenfassungen erstellt, haha!

Typen in go

Werfen wir zunächst einen Blick auf die grundlegendsten zentralisierten Typen, die in go enthalten sind

  • Grundlegende Typen: Zu den grundlegendsten Typen in go gehören Ganzzahlen (int, uint, int8, uint8, int16, uint16, int32, uint32, int64, uint64, byte, rune usw.), Gleitkommatyp (float32, float64), String (string ist ebenfalls vorhanden). a []rune Arrays) und weniger häufig verwendete komplexe Typen (complex64/complex128). int、uint、int8、uint8、int16、uint16、int32、uint32、int64、uint64、byte、rune等)、浮点型(float32、float64)、字符串(string也是个[]rune数组)和比较不常用的复数类型(complex64/complex128)。
  • 复合类型:主要包括结构体数组
  • 引用类型:Slice、Map、Channel、指针
  • 接口类型:Error、io.Reader等

go作为强类型语言并不会和PHP等高级语言自动帮我们进行类型转换,所以我们在比较时必须用==两边的类型必须一致,即使他们底部类型一致也不行。看下面的代码

package main
import "fmt"
type A struct {
    Id int
}
type B struct {
    Id int
}
func main() {
    var a int
    var b int16
    // 编译报错:invalid operation a == b (mismatched types int and int16)
    fmt.Println(a == b)
    aStruct := A{Id:5}
    bStruct := B{Id:5}
    // 编译报错:invalid operation: aStruct == bStruct (mismatched types A and B)
    fmt.Println(aStruct == bStruct)
}

所以go不会帮我们做隐式转换,即使底层的类型一致,也不能比较。
接下来我们从不同的类型来分析是否可以进行比较。

基本类型

go的基本类型就比较简单,只要类型是一样的,那么他们就是可以比较的,举个栗子:

package main
import "fmt"
func main() {
    var a int = 0
    var b int = 1
    // 输出false
    fmt.Println(a == b)
}

不过基本类型中也要注意浮点型的比较就不像我们现实中的一样,比如0.1+0.2在计算中运行结果就不是0.3了,而是0.30000000000000004了

package main
import "fmt"

func main() {
    var a float64=0.1
    var b float64=0.2
    // 0.30000000000000004
    fmt.Println(a+b)
}

为什么会这样,可以看下draveness(https://github.com/draveness) 大佬的这篇文章https://draveness.me/whys-the...

复合类型

数组

面试中也经常会问到go数组和切片的区别。数组在go中是必须先确定长度的,也就是长度不能再去扩容。并且它是个值拷贝,做参数传到一个函数中被修改,那么外部的值还是一样的不变的。Slice则相反。那么数组是否可以比较呢,看下面的例子:

package main
import "fmt"
func main() {
    a := [2]int{1, 2}
    b := [2]int{1, 2}
    c := [2]int{1, 3}
    d := [3]int{1, 2, 4}
    fmt.Println(a == b) // true
    fmt.Println(a == c) // false
    fmt.Println(a == d) // invalid operation: a == d (mismatched types [2]int and [3]int)
}

可以看出,相同长度的数组是可以比较的,而不同长度的数组是不能进行比较的。原因是什么呢?这是因为数组类型中,数组的长度也是类型的一部分,不同长度的数组那么他们的类型也就被认为不同的,所以无法比较。

结构体

同样的Struct也是一样的。Struct的比较也从内部类型开始比较,每一类型的值相等才是相等的。如下例子:

package main
import "fmt"
type A struct {
    id int
    name string
}
func main() {
    a := A{id:5,name:"123"}
    b := A{id:5,name:"123"}
    c := A{id:5,name:"1234"}
    fmt.Println(a == b) // true
    fmt.Println(a == c) // false
}

那么可以理解成Struct结构体是可以比较的吗。我们再来看个例子:

package main
import "fmt"
type A struct {
    id int
    name string
    son []int
}
func main() {
    a := A{id:5,name:"123",son:[]int{1,2,3}}
    b := A{id:5,name:"123",son:[]int{1,2,3}}
    fmt.Println(a == b) // invalid operation: a == b (struct containing []int cannot be compared)
}

怎么又变成不可比较的呢?这就要看下面的引用类型了。

引用类型

上面中的例子结构体中带上切片就无法比较了,在go中SliceMap被定义成不能比较的类型。我们来看

如果Slice是可比较,那么用什么来定义是一样的切片呢?如果用地址,那么如果两个地址指向的Slice是一样的呢?这显然不合适。如果和数组一样的方式,那么我切片扩容了呢,就不相等了。所以长度和容量导致不好比较。虽然可以在语言层面解决这个问题,但是 golang 团队认为不值得为此耗费精力。所以Slice被当成不可比较。
同样的Map

Zusammengesetzte Typen: umfassen hauptsächlich Struktur und Array.

Referenztypen: Slice, Map, Channel, Pointer.
  • Schnittstellentyp: Fehler, io.Reader usw..
  • Go als stark typisierte Sprache hilft uns nicht automatisch bei der Typkonvertierung mit Hochsprachen wie PHP, daher müssen wir beim Vergleich die Typen auf beiden Seiten verwenden Seien Sie konsistent, auch wenn ihre unteren Typen konsistent sind. Schauen Sie sich den Code unten an
package main
import "fmt"
type A struct {
    id int
    name string
}
func main() {
    a := &A { a : 1, b : "test1" }
    b := &A { a : 1, b : "test1" }
    c := a
    fmt.Println(a == b) // false
    fmt.Println(a == c) // true
    ch1 := make(chan int, 1)
    ch2 := make(chan int, 1)
    ch3 := ch1
    fmt.Println(ch1 == ch2) // false
    fmt.Println(ch1 == ch3) // true
}

Go führt also keine implizite Konvertierung für uns durch. Auch wenn die zugrunde liegenden Typen konsistent sind, können sie nicht verglichen werden.
Als nächstes analysieren wir, ob Vergleiche verschiedener Typen durchgeführt werden können.

Basistypen

🎜Gos Basistypen sind relativ einfach, dann können sie verglichen werden: 🎜
type eface struct { // 16 字节
    _type *_type
    data  unsafe.Pointer
}

type iface struct { // 16 字节
    tab  *itab
    data unsafe.Pointer
}
🎜Wir sollten jedoch auch auf den Vergleich von Gleitkommatypen in Basic achten Es ist nicht wie bei uns. Wenn zum Beispiel 0,1+0,2 berechnet wird, ist das Ergebnis nicht 0,3, sondern 0,3000000000000004🎜
var a interface{} = 0
var b interface{} = 2
var c interface{} = 0
var d interface{} = 0.0
fmt.Println(a == b) // false
fmt.Println(a == c) // true
fmt.Println(a == d) // false
🎜Warum ist das so? Sie können diesen Artikel von Draveness lesen ://github.com/draveness) Artikel https://draveness.me/whys-the...🎜🎜Zusammengesetzter Typ🎜

Array

🎜Der Unterschied zwischen Go-Arrays und Slices wird oft in Interviews gefragt . Die Länge eines Arrays in Go muss zuerst bestimmt werden, das heißt, die Länge kann nicht erweitert werden. Und es handelt sich um eine Wertkopie. Wenn die Parameter an eine Funktion übergeben und geändert werden, bleibt der externe Wert gleich. Slice ist das Gegenteil. Ob Arrays verglichen werden können, sehen Sie sich das folgende Beispiel an: 🎜
type A struct {
    a int
    b string
}

var a interface{} = A { a: 1, b: "test" }
var b interface{} = A { a: 1, b: "test" }
var c interface{} = A { a: 2, b: "test" }

fmt.Println(a == b) // true
fmt.Println(a == c) // false

var d interface{} = &A { a: 1, b: "test" }
var e interface{} = &A { a: 1, b: "test" }
fmt.Println(d == e) // false
🎜 Es ​​ist ersichtlich, dass Arrays gleicher Länge verglichen werden können, Arrays unterschiedlicher Länge jedoch nicht verglichen werden können. Was ist der Grund? Dies liegt daran, dass im Array-Typ auch die Länge des Arrays Teil des Typs ist. Arrays unterschiedlicher Länge werden als unterschiedliche Typen betrachtet und können daher nicht verglichen werden. 🎜

Struktur

🎜Die gleiche Struktur ist dieselbe. Der Vergleich von Struct beginnt ebenfalls mit dem internen Typ, und die Werte jedes Typs sind nur dann gleich, wenn sie gleich sind. Das folgende Beispiel: 🎜
var a interface{} = []int{1, 2}
var b interface{} = []int{1, 2}
// panic: runtime error: comparing uncomparable type []int
fmt.Println(a == b)
🎜Es ist also verständlich, dass Struct-Strukturen vergleichbar sind. Schauen wir uns ein anderes Beispiel an: Wie ist 🎜
var f *os.File

var r io.Reader = f
var rc io.ReadCloser = f
fmt.Println(r == rc) // true

var w io.Writer = f
// invalid operation: r == w (mismatched types io.Reader and io.Writer)
fmt.Println(r == w)
🎜 wieder unvergleichlich geworden? Dies hängt vom unten aufgeführten Referenztyp ab. 🎜🎜Referenztyp🎜🎜Die obige Beispielstruktur kann nicht verglichen werden, wenn sie Slices enthält. In go sind Slice und Map als nicht vergleichbare Typen definiert. Werfen wir einen Blick darauf🎜🎜Wenn Slice vergleichbar ist, was wird dann verwendet, um dieselben Slices zu definieren? Wenn Adressen verwendet werden, was passiert, wenn der Slice, auf den die beiden Adressen zeigen, identisch ist? Das ist offensichtlich unangemessen. Wenn es mit dem Array identisch ist und ich das Slice erweitere, ist es nicht gleich. Daher sind Länge und Kapazität schwer zu vergleichen. Obwohl dieses Problem auf Sprachebene gelöst werden kann, ist das Golang-Team der Ansicht, dass sich der Aufwand nicht lohnt. Daher wird Slice als unvergleichbar behandelt.
Die gleiche Map wird auch als unvergleichlicher Typ definiert. Sind Referenztypen also nicht vergleichbar? Nein, schauen Sie sich ein Beispiel an: 🎜
type ReadCloser interface {
    Reader
    Closer
}
🎜Referenztypvariablen speichern die Speicheradresse einer bestimmten Variablen. Daher bestimmt der Vergleich von Referenztypvariablen, ob die beiden Referenztypen dieselbe Variable speichern. 🎜🎜🎜Wenn es sich um dieselbe Variable handelt, muss die Speicheradresse dieselbe sein, dann sind die Referenztypvariablen gleich. Verwenden Sie „==“, um als wahr zu beurteilen.🎜🎜Wenn es sich nicht um dieselbe Variable handelt, muss die Speicheradresse unterschiedlich sein , „==" Ergebnis ist falsch🎜🎜🎜Schnittstellentyp🎜🎜Die Go-Sprache unterteilt Schnittstellentypen in zwei Kategorien, je nachdem, ob der Schnittstellentyp eine Reihe von Methoden enthält:🎜
  • 使用 runtime.iface结构体表示包含方法的接口
  • 使用 runtime.eface结构体表示不包含任何方法的 interface{} 类型
type eface struct { // 16 字节
    _type *_type
    data  unsafe.Pointer
}

type iface struct { // 16 字节
    tab  *itab
    data unsafe.Pointer
}

所以我们可以得知,一个接口值是由两个部分组成的,即该接口对应的类型和接口对应具体的值。接口值的比较涉及这两部分的比较,只有当类型和值都相等(动态值使用==比较),两个接口值才是相等的。看个例子:

var a interface{} = 0
var b interface{} = 2
var c interface{} = 0
var d interface{} = 0.0
fmt.Println(a == b) // false
fmt.Println(a == c) // true
fmt.Println(a == d) // false

ac类型相同(都是int),值也相同(都是0,基本类型比较),故两者相等。 ab类型相同,值不等,故两者不等。 ad类型不同,aintdfloat64,故两者不等。

type A struct {
    a int
    b string
}

var a interface{} = A { a: 1, b: "test" }
var b interface{} = A { a: 1, b: "test" }
var c interface{} = A { a: 2, b: "test" }

fmt.Println(a == b) // true
fmt.Println(a == c) // false

var d interface{} = &A { a: 1, b: "test" }
var e interface{} = &A { a: 1, b: "test" }
fmt.Println(d == e) // false

ab类型相同(都是A),值也相同(结构体A),故两者相等。 ac类型相同,值不同,故两者不等。 de类型相同(都是*A),值使用指针(引用)类型的比较,由于不是指向同一个地址,故不等。

不过需要注意的是,如果接口中类型是切片或者Map不可比较的类型,那么会直接报错的。看个例子:

var a interface{} = []int{1, 2}
var b interface{} = []int{1, 2}
// panic: runtime error: comparing uncomparable type []int
fmt.Println(a == b)

ab的类型是切片类型,而切片类型不可比较,所以a == bpanic

接口值的比较不要求接口类型(注意不是动态类型)完全相同,只要一个接口可以转化为另一个就可以比较。例如:

var f *os.File

var r io.Reader = f
var rc io.ReadCloser = f
fmt.Println(r == rc) // true

var w io.Writer = f
// invalid operation: r == w (mismatched types io.Reader and io.Writer)
fmt.Println(r == w)

r的类型为io.Reader接口,rc的类型为io.ReadCloser接口。查看源码,io.ReadCloser的定义如下:

type ReadCloser interface {
    Reader
    Closer
}

io.ReadCloser可转化为io.Reader,故两者可比较。

io.Writer不可转化为io.Reader,编译报错。

总结

  • 可比较:int、ifloat、string、bool、complex、pointe、channel、interface、array
  • 不可比较:slice、map、function
  • 复合类型中如果带有不可比较的类型,那么该类型也是不可比较的。可以理解不可比较类型具有传递性。

Das obige ist der detaillierte Inhalt vonAnalysieren Sie den Typvergleich in go. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:segmentfault.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen