github.com/google/go-cmp/cmp包

564 阅读3分钟

1.结构体中有未导出字段

type Address struct {
   Province string
   city     string //未导出字段
}

type User2 struct {
   Name    string
   Age     int
   Address Address
}

func main() {
   u1 := User2{"dj", 18, Address{Province: "0", city: "1"}}
   u2 := User2{"dj", 18, Address{Province: "0", city: "2"}}
   fmt.Println("u1 equals u2?", cmp.Equal(u1.Address, u2.Address, cmpopts.IgnoreUnexported(Address{}))) //true
   fmt.Println("u1 equals u2?", cmp.Equal(u1.Address, u2.Address, cmp.AllowUnexported(Address{})))      //false

   //fmt.Println("u1 equals u2?", cmp.Equal(u1, u2))
   //会直接panic,因为cmp.Equal()比较的两个结构体里面不能有未导出字段
   //这种情况下,如果笼统使用cmpopts.IngoreUnexported(User2{})还是会panic,因为city准确来说是Address中的未导出字段,而非User2的直接字段。

   fmt.Println("u1 equals u2?", cmp.Equal(u1, u2, cmpopts.IgnoreUnexported(Address{}))) //true
   //cmpopts.IgnoreUnexported(Address{})可以在对比两个对象的时候,不进行Address{}的未导出字段对比

   fmt.Println("u1 equals u2?", cmp.Equal(u1, u2, cmp.AllowUnexported(Address{}))) //false
   //我们也可以使用cmdopts.AllowUnexported(Address{})表示需要比较Address的未导出字段:
}

2.结构体中字段都可导出有指针变量

type Contact struct {
   Phone string
   Email string
}

type User struct {
   Name    string
   Age     int
   Contact *Contact
}

func main() {
   u1 := User{Name: "dj", Age: 18}
   u2 := User{Name: "dj", Age: 18}

   fmt.Println("u1 == u2?", u1 == u2)              //true
   fmt.Println("u1 equals u2?", cmp.Equal(u1, u2)) //true

   c1 := &Contact{Phone: "123456789", Email: "dj@example.com"}
   c2 := &Contact{Phone: "123456789", Email: "dj@example.com"}

   u1.Contact = c1
   u2.Contact = c2
   fmt.Println("u1 == u2 with different pointer?", u1 == u2)              //false【结构体里面带有指针的地址不同就不等】
   fmt.Println("u1 equals u2 with different pointer?", cmp.Equal(u1, u2)) //true【不看地址,只关注值一不一样】
}

cmp.Equal()与reflect.DeepEqual的不同

默认情况下,cmp.Equal()函数不会比较未导出字段,因为会直接panic(字段名首字母小写的字段叫未导出字段)

遇到未导出字段,cmp.Equal()直接panic,这一点与reflect.DeepEqual()有所不同,后者会比较未导出的字段。

3.浮点数的比较

go-cmp提供比较浮点数的选项,两个浮点数相差不超过一定范围就认为它们相等: cmpopts.EquateApprox(fraction, margin)
第一个参数的含义是取两个数绝对值的较小者,乘以fraction,如果两个数的差的绝对值小于这个数即|x-y| ≤ max(fraction*min(|x|, |y|), margin),则认为它们相等。
第二个参数比较好理解,如果两个浮点数的差的绝对值小于margin则认为它们相等。

如果fractionmargin同时设置,只需要满足一个就行了

type FloatPair struct {
   X float64
}

func main() {
   f1 := 0.1
   f2 := 0.2
   f3 := 0.3
   p3 := FloatPair{X: f1 + f2}
   p4 := FloatPair{X: f3}
   fmt.Printf("p3=%v p4=%v\n", p3, p4) //p3={0.30000000000000004} p4={0.3}【由于变量的存储空间,会存在误差】
   fmt.Println("p3 == p4?", p3 == p4)                                                //false
   fmt.Println("p3 equals p4?", cmp.Equal(p3, p4))                                   //false
   fmt.Println("p3 equals p4?", cmp.Equal(p3, p4, cmpopts.EquateApprox(0.1, 0.001))) //true
   //一般情况下,满足第一个误差就可以了,因为 0.0..04 明显小于 0.1 所以 true
}

4. nil切片 与 空切片 有时候就是想要忽略他们的有无地址

  • 默认情况下,如果一个切片变量值为nil,另一个是使用make创建的长度为 0 的切片,
  • 那么go-cmp认为它们是不等的。
  • 同样的,一个map变量值为nil,一个是使用make创建的长度为 0 的map,go-cmp也认为它们不等。
  • 我们可以指定cmpopts.EquateEmpty选项,让go-cmp认为它们相等
func main() {
   var s1 []int            //没地址没值
   var s2 = make([]int, 0) //有地址没值

   var m1 map[int]int         //没地址没值
   var m2 = make(map[int]int) //有地址没值

   //fmt.Println(s1==s2)//直接编译报错
   //fmt.Println(m1==m2)//直接编译报

   fmt.Println("s1 equals s2?", cmp.Equal(s1, s2))
   fmt.Println("m1 equals m2?", cmp.Equal(m1, m2))

   //相当于cmpopts.EquateEmpty()忽略了有无地址的不同
   //没地址没值,和有地址没值本质上是不一样的
   fmt.Println("s1 equals s2 with option?", cmp.Equal(s1, s2, cmpopts.EquateEmpty()))
   fmt.Println("m1 equals m2 with option?", cmp.Equal(m1, m2, cmpopts.EquateEmpty()))
}

5.无序切片的元素

func main() {
   //默认情况下,两个切片只有当长度相同,且对应位置上的元素都相等时,go-cmp才认为它们相等。
   //如果,我们想要实现无序切片的比较(即只要两个切片包含相同的值就认为它们相等),
   //可以使用cmpopts.SortedSlice选项先对切片进行排序,然后再进行比较:
   s1 := []int{1, 2, 3, 4}
   s2 := []int{4, 3, 2, 1}
   s3 := make([]int, 5, 10)
   s3 = s1[:]
   fmt.Println("s1 equals s2?", cmp.Equal(s1, s2))                                                                       //false
   fmt.Println("s1 equals s2 with option?", cmp.Equal(s1, s2, cmpopts.SortSlices(func(i, j int) bool { return i < j }))) //true
   fmt.Println("s2 equals s3 with option?", cmp.Equal(s2, s3, cmpopts.SortSlices(func(i, j int) bool { return i < j }))) //true
   //可以发现s2 和 s3 的长度容量显然不一致,但是 cmp.Equal(cmpopts.SortSlices) 还是判了相等,所以cmp.Equal(cmpopts.SortSlices)只管内容排序后实际的元素,至于长度,容量不管

   m1 := map[int]int{1: 10, 3: 30, 2: 20}
   m2 := map[int]int{1: 10, 2: 20, 3: 30}
   fmt.Println("m1 equals m2?", cmp.Equal(m1, m2))               //true【可以发现map本身就不存在顺序可言,所以不用排序就可以认为相等】
   fmt.Println("m1 equals m2 with option?", cmp.Equal(m1, m2, cmpopts.SortMaps(func(i, j int) bool { return i < j }))) //true
}

转载自[blog.csdn.net/weixin_3982…]