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

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

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

A是接口,BCD都是接口A.

的类型

我有一片变量args,所有类型都带有接口A,每个都可以是BCD类型具体来说。

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

我写了下面的代码:

var existingTyp A
for i, d := range args {
 switch typ := d.(type) {
   case *B, *C, *D:
    if existingTyp == nil {
        existingTyp = typ
    } else {
        if typ != existingTyp {
            panic("error!")
        }
   }
}

如何修改代码来实现我想要的?

您不能对接口值使用相等运算符 ==。即使动态类型相同,如果它们具有不同值的字段,比较也可能 return false。或者,如果 BCD 与开始时不可比,它会出现恐慌。

相反,您可以使用反射并在 reflect.Type 上使用 ==。如果您添加更多实现 A.

的类型,此解决方案不需要您更新代码
func dynamicTypesEq(args []A) bool {
    var a reflect.Type
    for _, d := range args {
        t := reflect.TypeOf(d)
        if a == nil {
            a = t
            continue
        }
        if a != t {
            return false
        }

    }
    return true
}

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

func main() {
    a := []A{&B{}, &B{}, &B{}}
    fmt.Println(dynamicTypesEq(a)) // true

    b := []A{&C{}, &B{}, &B{}}
    fmt.Println(dynamicTypesEq(b)) // false


    c := []A{&D{}, &D{}, &B{}}
    fmt.Println(dynamicTypesEq(c)) // false
}

请注意,如果输入有 *BB,此函数会报告错误。显然,指针类型与基类型不同。

游乐场:https://go.dev/play/p/QOCvSyxGPRU

以下是使用反射包的方法。如果某个元素的类型与第一个元素的类型不同,则切片包含混合值类型。

func check(args []interface{}) bool {
    if len(args) == 0 {
        return true
    }
    t := reflect.TypeOf(args[0])
    for _, v := range args[1:] {
        if reflect.TypeOf(v) != t {
            return false
        }
    }
    return true
}

下面是不反射的方法。保留一个状态变量,记录最后看到的类型。如果当前类型不是最后一个类型,则切片包含混合值类型。

func check(args []interface{}) bool {
    const (
        initState = iota
        typeB
        typeC
        typeD
    )
    state := initState
    for _, d := range args {
        switch d.(type) {
        case *B:
            if state != initState && state != typeB {
                return false
            }
            state = typeB
        case *C:
            if state != initState && state != typeC {
                return false
            }
            state = typeC
        case *D:
            if state != initState && state != typeD {
                return false
            }
            state = typeD
        default:
            panic("unsupported type")
        }
    }
    return true
}