当前位置: 首页 > article >正文

十分钟掌握Go语言==运算符与reflect.DeepEqual函数处理interface{}值的比较规则

在 Go 语言中,interface{} 类型是一种特殊的接口类型,它表示任意类型的值。你可以使用 == 运算符来检测任意两个 interface{} 类型值的相等性,比较的规则和一般的接口类型一样,需要满足以下条件:

  • 两个 interface{} 值的动态类型必须相同,也就是说,它们存储的具体类型必须一致。
  • 两个 interface{} 值的动态值必须相等,也就是说,它们存储的具体值必须可以比较的。

根据 Go 语言规范的描述,interface{} 类型的值在内存中是由两个字(word)组成的,一个字存储了动态类型的信息,另一个字存储了动态值的信息。如果动态值的大小超过了一个字,那么这个字就存储了动态值的指针,指向实际的动态值。

Go 编译器是如何判定 interface{} 值是否相等的呢?

使用 == 符号比较两个 interface{} 类型的值时,Go编译器会先比较它们的动态类型,也就是比较它们的第一个字,看是否指向相同的类型信息。如果不相同直接返回 false,如果相同,则继续比较它们的动态值,也就是比较它们的第二个字,看是否相等。如果动态值的大小超过了一个字,那么这个字就是一个指针,需要根据指针找到实际的动态值,并且根据动态类型的规则进行比较。

例如,如果动态类型是数组,那么就需要逐个比较数组的元素,如果动态类型是结构体,那么就需要逐个比较结构体的字段,以此类推。

下面是一个简单的例子,演示了如何使用 == 符号比较两个 interface{} 类型的值:

package main

import "fmt"

func main() {
	var a, b interface{}
	// 给 a 赋值为 int64 类型的 1
	a = int64(1)
	// 给 b 赋值为 int64 类型的 2
	b = int64(2)
	// a和b的动态类型相同, 动态值不同, 输出结果为 false
	fmt.Println(a == b) // false

	// 给 b 赋值为 int64 类型的 1
	b = int64(1)
	// a和b的动态类型相同, 动态值也相同, 此时输出结果为true
	fmt.Println(a == b) // true

	// 给 b 赋值为 float32 类型的 1
	b = float32(1)
	// 此时a和b的动态类型不相同, 就不会进行动态值的处理, 输出结果为false
	fmt.Println(a == b) // false

	// 给 a,b 赋值为 int 类型的数组
	a = [5]int{1, 2, 3, 4, 5}
	b = [5]int{1, 2, 3, 4, 5}
	fmt.Println(a == b) // true

	// 给 b 赋值为 int 类型的数组
	b = [5]int{1, 2, 3, 4, 6}
	fmt.Println(a == b) // false

	// 给 b 赋值为 int 类型的切片
	b = []int{1, 2, 3, 4, 5}
	// 此时a和b的动态类型已然不相同, 就不会进行动态值的处理, 输出结果为false
	fmt.Println(a == b) // false

	// 给 a 赋值为 int 类型的切片
	a = []int{1, 2, 3, 4, 5}
	// 此时a和b的动态类型和动态值虽然相同, 但是在Go语言中切片类型的值是不能用 == 符号比较,会引发运行时错误
	//fmt.Println(a == b) // panic: runtime error: comparing uncomparable type []int
}

== 运算符通常只能用于比较基本类型和支持 == 操作的复合类型,如数组、结构体等,而不能用于比较切片、映射、函数等类型,否则会引发编译错误或运行时错误。

那么问题来了,如果一定要比较两个切片、映射、函数的相等性,该如何操作呢?

reflect.DeepEqual 函数原型:

// 用于判断两个值是否深度一致
// 
// 除了类型相同;在可以时(主要是基本类型)会使用==;但还会比较array、slice的成员,
// map的键值对,结构体字段进行深入比对。map的键值对,对键只使用==,但值会继续往深
// 层比对。DeepEqual函数可以正确处理循环的类型。函数类型只有都会nil时才相等;空切
// 片不等于nil切片;还会考虑array、slice的长度、map键值对数。
func DeepEqual(x, y any) bool

对于 reflect.DeepEqual 而言,它通过牺牲程序的性能来弥补 == 运算符无法处理切片、映射、函数的短板,对于不支持 == 操作的类型,reflect.DeepEqual 函数会有一套自己的比较规则。

你可以把 reflect.DeepEqual 函数理解成是 == 运算符的扩展版!下面是一个简单的例子,演示了如何使用 reflect.DeepEqual 函数:

package main

import "fmt"

func main() {
	var a, b interface{}
	// 给 a 赋值为 int64 类型的 1
	a = int64(1)
	// 给 b 赋值为 int64 类型的 2
	b = int64(2)
	fmt.Println(a == b, reflect.DeepEqual(a, b))

	b = int64(1)
	fmt.Println(a == b, reflect.DeepEqual(a, b))

	b = float32(1)
	fmt.Println(a == b, reflect.DeepEqual(a, b))

	// 给 b 赋值为 int 类型的切片
	a, b = []int{1, 2, 3, 4, 5}, []int{1, 2, 3, 4, 5}
	fmt.Println(reflect.DeepEqual(a, b)) // true

	b = []int{5, 4, 3, 2, 1}
	fmt.Println(reflect.DeepEqual(a, b)) // false
}

 


http://www.kler.cn/a/229053.html

相关文章:

  • 单元测试与unittest框架
  • 【C++】构造函数与析构函数
  • 使用docker-compose安装ELK(elasticsearch,logstash,kibana)并简单使用
  • SDK调用文心一言如何接入,文心一言API接入教程
  • Jmeter进行http接口并发测试
  • 低代码独特架构带来的编译难点及多线程解决方案
  • C++泛型编程:类模板(下)
  • 【Iceberg学习一】什么是Iceberg?
  • 飞天使-k8s知识点12-kubernetes散装知识点1-架构有状态资源对象分类
  • PostgreSQL解决序列(自增id)自动增长冲突
  • 电路设计(10)——超温报警电路的proteus仿真
  • Windows10安装VScode + mingw64 + GSL
  • 【C++搜索二叉树】
  • HuggingFace库中BERTForxxx模型代码详细分析 使用BERT进行无监督预训练
  • 第97讲:MHA高可用集群模拟主库故障以及修复过程
  • Java大致面试题及答案,文档格式为md格式
  • 【计算机二级考试C语言】C排序算法
  • 各种编程语言送祝福:2024龙年大吉
  • sqli.labs靶场(54-65关)
  • 适用于 Windows 和 Mac 的 16 款最佳数据恢复软件
  • 软件漏洞概念与原理
  • Zookeeper相关面试准备问题
  • 改变终端安全的革命性新兴技术:自动移动目标防御技术AMTD
  • 【自定义序列化器】⭐️通过继承JsonSerializer和实现WebMvcConfigurer类完成自定义序列化
  • oracle视图二次查询慢
  • C++类与对象(下)