如何在Golang中根据字段而不是结构体类型确定结构体的相等性?

3

我目前正在使用go-cmp包来比较结构体的相等性。出于测试目的,我需要比较两种不同类型的结构体,这些结构体应该具有相同的字段和值。

作为一个最简示例,我遇到了这样的问题:cmp.Equal()函数对于不同的类型返回false,即使它们具有相同的字段和值。

type s1 struct {
    Name string
}

type s2 struct {
    Name string
}

p1 := s1{Name: "John"}
p2 := s2{Name: "John"}

fmt.Println(cmp.Equal(p1, p2)) // false

这是可以理解的,因为这两种类型是不同的,但是否有一种方法可以指示cmp.Equal()忽略类型,只查看字段呢?

3个回答

2

我不知道在比较时是否可以省略类型,但如果两个结构体类型具有相同的字段,则可以将一个类型转换为另一个类型,因此这不会成为问题:

p1 := s1{Name: "John"}
p2 := s2{Name: "John"}

fmt.Println(cmp.Equal(p1, p2)) // false
fmt.Println(cmp.Equal(p1, s1(p2))) // true

Go Playground上试一试。


在这种情况下,将一种类型转换为另一种类型对我来说行不通。一种类型来自外部库,它包含未导出的字段,而另一种类型则是我自己的。 - Loupi

0
我建议从长远考虑,编写一个函数IsS1EqualToS2,逐个检查字段:
func IsS1EqualToS2(s1 s1, s2 s2) bool {
    if s1.Name != s2.Name {
        return false
    }
    return true
}

并使用如下方式: IsS1EqualToS2(p1, p2)


int(1)string("1")具有相同的字符串表示。 - icza
1
@icza 没错,但问题不是关于两个结构体在相同顺序下共享完全相同的字段吗?第一种解决方案(字符串相等性)是检查相等性的另一种替代方式,而不是使用 cmp.Equal(p1, s1(p2))。如果一个结构体中的字段是整数,而另一个结构体中的字段是字符串,则该问题无效。因为它明确要求两个具有相同字段的结构体。 - Davud Safarov
如果结构体有一个类型为[]any的字段,第一个结构体持有{1,"1"},而另一个持有{"1",1},您的方法将它们视为相等,即使它们实际上不相等。这只是比较字符串表示中许多陷阱的一个示例。 - icza
我的第一个答案是针对名为s1和s2的两个结构体,它们都有“Name string”字段。正如我在答案中所写的那样,更好的做法是创建一个名为“IsS1EqualToS2”的函数,这将使比较S1和S2变得容易,因为应用程序增长时它们具有不同的结构。但我确信第一个答案是OP正在寻找的。这是一种快速检查特定S1和S2结构体相等性的方法。 - Davud Safarov
我相信s1s2只是[mcve]的示例,而不是提问者真正的类型,但如果我错了,@Loupi可以纠正我。 - icza
啊,我明白了。那么这就有意义了。但只要结构体不使用通用字段,我认为字符串相等性就不会给出错误的答案。但它仍然可能会引起混淆,所以我将从答案中删除该部分。谢谢。 - Davud Safarov

0

以下是一种通过 json.Marshaljson.Unmarshalinterface 类型来比较字段而不考虑它们的类型的方法。

    type s1 struct {
        Name string
    }

    type s2 struct {
        Name string
    }

    p1 := s1{Name: "John"}
    p2 := s2{Name: "John"}

    fmt.Println(cmp.Equal(p1, p2)) // false

    var x1 interface{}
    b1, _ := json.Marshal(p1)
    _ = json.Unmarshal(b1, &x1)

    var x2 interface{}
    b2, _ := json.Marshal(p2)
    _ = json.Unmarshal(b2, &x2)

    fmt.Println(cmp.Equal(x1, x2)) // true

示例游乐场


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接