成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

一篇學會Go 語言類型可比性

開發
在 Go 中,數據類型可以被分為兩類,可比較與不可比較。兩者區分非常簡單:類型是否可以使用運算符 == 和 != 進行比較。

[[434973]]

前段時間,一位讀者私信了我一個 Go 代碼例子,并問我這是不是一個 bug。我覺得蠻有意思的,故整理出了本文的分享內容。

在討論代碼之前,讀者需要有一些前置知識。

Go 可比較類型

在 Go 中,數據類型可以被分為兩類,可比較與不可比較。兩者區分非常簡單:類型是否可以使用運算符 == 和 != 進行比較。

那哪些類型是可比較的呢?

  • Boolean(布爾值)、Integer(整型)、Floating-point(浮點數)、Complex(復數)、String(字符)這些類型是毫無疑問可以比較的。
  • Poniter (指針) 可以比較:如果兩個指針指向同一個變量,或者兩個指針類型相同且值都為 nil,則它們相等。注意,指向不同的零大小變量的指針可能相等,也可能不相等。
  • Channel (通道)具有可比性:如果兩個通道值是由同一個 make 調用創建的,則它們相等。
  1. c1 := make(chan int, 2) 
  2.  
  3. c2 := make(chan int, 2) 
  4.  
  5. c3 := c1 
  6.  
  7. fmt.Println(c3 == c1) // true 
  8.  
  9. fmt.Println(c2 == c1) // false 
  • Interface (接口值)具有可比性:如果兩個接口值具有相同的動態類型和相等的動態值,則它們相等。
  • 當類型 X 的值具有可比性且 X 實現 T 時,非接口類型 X 的值 x 和接口類型 T 的值 t 具有可比性。如果 t 的動態類型與 X 相同且 t 的動態值等于 x,則它們相等。
  • 如果所有字段都具有可比性,則 struct (結構體值)具有可比性:如果它們對應的非空字段相等,則兩個結構體值相等。
  • 如果 array(數組)元素類型的值是可比較的,則數組值是可比較的:如果它們對應的元素相等,則兩個數組值相等。

哪些類型是不可比較的?

  • slice、map、function 這些是不可以比較的,但是也有特殊情況,那就是當他們值是 nil 時,可以與 nil 進行比較。

動態類型

在上文接口可比性中,我們提到了動態類型與動態值,這里需要介紹一下。

常規變量(非接口)的類型是由聲明所定義,這是靜態類型,例如 var x int。

接口類型的變量有一個獨特的動態類型,它是運行時存儲在變量中的值的實際類型。動態類型在執行過程中可能會有所不同,但始終可以分配給接口變量一個靜態類型。

例如

  1. var someVariable interface{} = 101 

someVariable 變量的靜態類型是 interface{},但是它的動態類型是 int,并且很可能在之后發生變化。

  1. var someVariable interface{} = 101 
  2. someVariable = 'Gopher' 

如上, someVariable 變量的動態類型從 int 變為了 string。

代碼場景示例

我們為當前業務所需的數據模型定義一個結構體 Data,它包含兩個字段:一個 string 類型的 UUID 和 interface{} 類型的 Content。

  1. type Data struct { 
  2.  UUID    string 
  3.  Content interface{} 

根據上文介紹, string 類型和 interface 是可比較類型,那么兩個 Data 類型的數據,我們可以通過 == 操作符進行比較。

  1. var x, y Data 
  2. x = Data{ 
  3.  UUID:    "856f5555806443e98b7ed04c5a9d6a9a"
  4.  Content: 1, 
  5. y = Data{ 
  6.  UUID:    "745dee7719304991862e6985ea9c02a9"
  7.  Content: 2, 
  8. fmt.Println(x == y) 

但是,如果在 Content 中的動態類型是 map 會怎樣?

  1. var m, n Data 
  2. m = Data{ 
  3.  UUID:    "9584dba3fe26418d86252d71a5d78049"
  4.  Content: map[int]string{1: "GO", 2: "Python"}, 
  5. n = Data{ 
  6.  UUID:    "9584dba3fe26418d86252d71a5d78049"
  7.  Content: map[int]string{1: "GO", 2: "Python"}, 
  8. fmt.Println(m == n) 

此時,我們程序編譯通過,但會發生運行時錯誤。

  1. panic: runtime error: comparing uncomparable type map[int]string 

那針對這種需求:即對于不可比較類型,因為不能使用比較操作符 ==,但我們想要比較它們包含的值是否相等時,應該怎么辦。

此時我們可以采用 reflect.DeepEqual 函數進行比較,即將上述的 m==n 替換

  1. fmt.Println(reflect.DeepEqual(m,n)) // true 

我們得出結論:如果我們的變量中包含不可比較類型,或者 interface 類型(它的動態類型可能存在不可比較的情況),那么我們直接運用比較運算符 == ,會引發程序錯誤。此時應該選用 reflect.DeepEqual 函數(當然也有特殊情況,例如 []byte,可以通過 bytes. Equal 函數進行比較)。

Bug 代碼?

好,鋪墊了這么久,終于可以展示讀者給我的代碼了。

  1. var x, y Data 
  2. x = Data{ 
  3.  UUID:    "856f5555806443e98b7ed04c5a9d6a9a"
  4.  Content: 1, 
  5. bytes, _ := json.Marshal(x) 
  6. _ = json.Unmarshal(bytes, &y) 
  7. fmt.Println(x)  // {856f5555806443e98b7ed04c5a9d6a9a 1} 
  8. fmt.Println(y)  // {856f5555806443e98b7ed04c5a9d6a9a 1} 
  9. fmt.Println(reflect.DeepEqual(x, y)) // false 

對于同一個原始數據,經過 json 的 Marshal 和 Unmarshal 過程后,竟然不相等了?難道有 bug?

不慌,這種時候,我們直接上調試看看。

debug

原來此 1 非彼 1,Content 字段的數據類型由 int 轉換為了 float64 。而在接口中,其動態類型不一致時,它的比較是不相等的。

經過排查,發現問題就出在 Unmarshal 函數上:如果要將 Json 對象 Unmarshal 為接口值,那么它的類型轉換規則如下

Unmarshal

可以看到,數值型的 json 解析操作統一為了 float64。

因此,如果我們將 Content: 1 改為 Content: 1.0 ,那么它 reflect.DeepEqual(x, y) 的值將是 true。

增強型 DeepEqual 函數

針對 json 解析的這種類型改變特性,我們可以基于 reflect.DeepEqual 函數進行改造適配。

  1. func DeepEqual(v1, v2 interface{}) bool { 
  2.  if reflect.DeepEqual(v1, v2) { 
  3.   return true 
  4.  } 
  5.  bytesA, _ := json.Marshal(v1) 
  6.  bytesB, _ := json.Marshal(v2) 
  7.  return bytes.Equal(bytesA, bytesB) 

當我們使用增強后的函數來運行上述的 “bug” 例子

  1. var x, y Data 
  2. x = Data{ 
  3.  UUID:    "856f5555806443e98b7ed04c5a9d6a9a"
  4.  Content: 1, 
  5. b, _ := json.Marshal(x) 
  6. _ = json.Unmarshal(b, &y) 
  7. fmt.Println(DeepEqual(x, y)) // true 

此時,結果符合我們的預期。

結論

本文討論了 Go 的可比較與不可比較類型,并對靜態、動態類型進行了闡述。

不可比較類型包括 slice、map、function,它們不能使用 == 進行比較。雖然我們可以通過 == 操作符對 interface 進行比較,由于動態類型的存在,如果實現 interface 的 T 有不可比較類型,這將引起運行時錯誤。

在不能確定 interface 的實現類型的情況下,對 interface 的比較,可以使用 reflect.DeepEqual 函數。

最后,我們通過 json 庫的解析與反解析過程中,發現了 json 解析存在數據類型轉換操作。這一個細節,讀者在使用過程中需要注意,以免產生想法“這代碼有 bug” 。

參考

https://golang.org/ref/spec#Comparison_operators

https://golang.org/ref/spec#Types

https://pkg.go.dev/encoding/json#Unmarshal

 

責任編輯:武曉燕 來源: Golang技術分享
相關推薦

2023-12-05 07:14:27

AIGo

2021-07-16 22:43:10

Go并發Golang

2012-04-16 09:34:54

IBM思科Orac

2022-05-17 08:02:55

GoTryLock模式

2021-12-09 07:13:25

C#集合類型

2022-06-09 08:41:17

Go網絡庫Gnet

2024-05-10 08:15:32

go語言反射機制

2022-01-02 08:43:46

Python

2021-10-09 07:10:31

Go語言基礎

2020-10-23 08:38:19

Go語言

2021-06-24 06:35:00

Go語言進程

2022-04-26 09:01:39

實用工具類型TypeScript

2022-02-07 11:01:23

ZooKeeper

2021-10-26 22:41:09

鴻蒙安卓系統

2021-10-16 10:17:51

Go語言數據類型

2021-05-11 08:54:59

建造者模式設計

2021-07-02 09:45:29

MySQL InnoDB數據

2021-07-06 08:59:18

抽象工廠模式

2023-01-03 08:31:54

Spring讀取器配置

2022-08-26 09:29:01

Kubernetes策略Master
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 九九精品在线 | 亚洲成人高清 | 91精品国产91久久久久久吃药 | 日韩中文字幕第一页 | 国产精品成人一区二区三区 | 2018天天干天天操 | 91精品国产自产在线老师啪 | 欧美a在线| 91亚洲精华国产 | 久久久久久久久久久久久久av | 久久亚洲欧美日韩精品专区 | 国产日韩一区二区三免费 | 国产精品美女www爽爽爽视频 | 成人欧美一区二区三区黑人孕妇 | 日韩一 | 亚洲风情在线观看 | 91一区二区 | 视频在线一区二区 | 国产精品久久久亚洲 | 久久中文字幕在线 | 亚洲精品欧洲 | www.久久久.com | 日本精品一区二区三区在线观看视频 | 精品视频一二区 | 欧美日韩在线国产 | 欧美国产日本一区 | 丁香色婷婷| 欧美日韩一区二区视频在线观看 | 最新中文字幕一区 | 日韩在线不卡 | 看av片网站 | 成人av电影免费在线观看 | 国产亚洲精品一区二区三区 | 中文字幕精品一区二区三区在线 | 成人高清在线视频 | 亚洲欧美中文日韩在线 | 日韩高清黄色 | 天天操网| 美女视频一区二区 | 久久久久久国产精品免费免费 | 国产一伦一伦一伦 |