如何检查切片接口元素是否具有相同的动态类型?

huangapple go评论105阅读模式
英文:

How to check if slice interface elements have the same dynamic type?

问题

我有以下的结构体,它们遵循这个结构:

A 是接口,BCD 都是类型,且实现了接口 A

我有一个变量切片 args,其中的每个变量都是类型为接口 A 的类型,具体可以是 BCD 中的一种。

我想写一个循环来判断切片中的所有变量是否都属于同一动态类型。

我写了以下代码:

  1. var existingTyp A
  2. for i, d := range args {
  3. switch typ := d.(type) {
  4. case *B, *C, *D:
  5. if existingTyp == nil {
  6. existingTyp = typ
  7. } else {
  8. if typ != existingTyp {
  9. panic("error!")
  10. }
  11. }
  12. }

如何修改代码以实现我想要的效果?

英文:

I have the following structs and they follow this structure:

A is the interface, and B, C, D are all types with interface A.

I have a slice of variables args all with type with interface A, each of which can be B, C, D type specifically.

I'd like to write a for loop to judge if all variables in the slice fall into the same dynamic type.

I wrote the following code:

  1. var existingTyp A
  2. for i, d := range args {
  3. switch typ := d.(type) {
  4. case *B, *C, *D:
  5. if existingTyp == nil {
  6. existingTyp = typ
  7. } else {
  8. if typ != existingTyp {
  9. panic("error!")
  10. }
  11. }
  12. }

How to revise the code to achieve what I want?

答案1

得分: 3

你不能在接口值上使用等号运算符==。即使动态类型相同,如果它们具有不同值的字段,比较可能返回false。或者如果BCD本身不可比较,则会引发panic。

相反,你可以使用反射,在reflect.Type上使用==。这种解决方案不需要在添加更多实现A的类型时更新代码。

  1. func dynamicTypesEq(args []A) bool {
  2. var a reflect.Type
  3. for _, d := range args {
  4. t := reflect.TypeOf(d)
  5. if a == nil {
  6. a = t
  7. continue
  8. }
  9. if a != t {
  10. return false
  11. }
  12. }
  13. return true
  14. }

使用一些示例切片调用该函数:

  1. func main() {
  2. a := []A{&B{}, &B{}, &B{}}
  3. fmt.Println(dynamicTypesEq(a)) // true
  4. b := []A{&C{}, &B{}, &B{}}
  5. fmt.Println(dynamicTypesEq(b)) // false
  6. c := []A{&D{}, &D{}, &B{}}
  7. fmt.Println(dynamicTypesEq(c)) // false
  8. }

请注意,如果输入中同时包含*BB,该函数将返回false。显然,指针类型与基本类型不同。

Playground链接:https://go.dev/play/p/QOCvSyxGPRU

英文:

You can't use the equality operator == on the interface values. Even if the dynamic types are the same, the comparison may return false if they have fields with different values. Or it panics if B, C and D aren't comparable to begin with.

Instead you can use reflection and use == on reflect.Type. This solution doesn't require you to update the code if you add more types that implement A.

  1. func dynamicTypesEq(args []A) bool {
  2. var a reflect.Type
  3. for _, d := range args {
  4. t := reflect.TypeOf(d)
  5. if a == nil {
  6. a = t
  7. continue
  8. }
  9. if a != t {
  10. return false
  11. }
  12. }
  13. return true
  14. }

Calling the function with some example slices:

  1. func main() {
  2. a := []A{&B{}, &B{}, &B{}}
  3. fmt.Println(dynamicTypesEq(a)) // true
  4. b := []A{&C{}, &B{}, &B{}}
  5. fmt.Println(dynamicTypesEq(b)) // false
  6. c := []A{&D{}, &D{}, &B{}}
  7. fmt.Println(dynamicTypesEq(c)) // false
  8. }

Note that this function reports false in case the input has *B and B. Clearly, a pointer type is not the same as the base type.

Playground: https://go.dev/play/p/QOCvSyxGPRU

答案2

得分: 2

以下是使用reflect包进行翻译的代码:

  1. // 使用reflect包进行检查
  2. func check(args []interface{}) bool {
  3. if len(args) == 0 {
  4. return true
  5. }
  6. t := reflect.TypeOf(args[0])
  7. for _, v := range args[1:] {
  8. if reflect.TypeOf(v) != t {
  9. return false
  10. }
  11. }
  12. return true
  13. }

以下是不使用reflect进行翻译的代码:

  1. // 不使用reflect进行检查
  2. func check(args []interface{}) bool {
  3. const (
  4. initState = iota
  5. typeB
  6. typeC
  7. typeD
  8. )
  9. state := initState
  10. for _, d := range args {
  11. switch d.(type) {
  12. case *B:
  13. if state != initState && state != typeB {
  14. return false
  15. }
  16. state = typeB
  17. case *C:
  18. if state != initState && state != typeC {
  19. return false
  20. }
  21. state = typeC
  22. case *D:
  23. if state != initState && state != typeD {
  24. return false
  25. }
  26. state = typeD
  27. default:
  28. panic("unsupported type")
  29. }
  30. }
  31. return true
  32. }

希望对你有帮助!

英文:

Here's how to do it using the reflect package. If type of some element is different from the type of the first element, then the slice contains mixed value types.

  1. func check(args []interface{}) bool {
  2. if len(args) == 0 {
  3. return true
  4. }
  5. t := reflect.TypeOf(args[0])
  6. for _, v := range args[1:] {
  7. if reflect.TypeOf(v) != t {
  8. return false
  9. }
  10. }
  11. return true
  12. }

Here's how to do it without reflect. Keep a state variable that records the last type seen. If the current type is not the last type, then the slice contains mixed value types.

  1. func check(args []interface{}) bool {
  2. const (
  3. initState = iota
  4. typeB
  5. typeC
  6. typeD
  7. )
  8. state := initState
  9. for _, d := range args {
  10. switch d.(type) {
  11. case *B:
  12. if state != initState && state != typeB {
  13. return false
  14. }
  15. state = typeB
  16. case *C:
  17. if state != initState && state != typeC {
  18. return false
  19. }
  20. state = typeC
  21. case *D:
  22. if state != initState && state != typeD {
  23. return false
  24. }
  25. state = typeD
  26. default:
  27. panic("unsupported type")
  28. }
  29. }
  30. return true
  31. }

huangapple
  • 本文由 发表于 2022年3月1日 14:31:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/71304558.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定