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

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

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

问题

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

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

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

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

我写了以下代码:

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!")
		}
   }
}

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

英文:

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:

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!")
		}
   }
}

How to revise the code to achieve what I want?

答案1

得分: 3

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

相反,你可以使用反射,在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,该函数将返回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.

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
}

Calling the function with some example slices:

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
}

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包进行翻译的代码:

// 使用reflect包进行检查
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
}

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

// 不使用reflect进行检查
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
}

希望对你有帮助!

英文:

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.

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
}

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.

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
}

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:

确定