Go类型开关中的代码重复

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

Code Duplication in Go Type Switches

问题

刚开始编写Go代码,遇到了一个有趣的问题。

在不重复编写代码的情况下,有没有一种简便的方法来迭代一个作为空接口传入的数组中的项?考虑以下代码:

function(someArr interface{}) {
  switch someArr.(type) {
    case []int :
        arr := (someArr).([]int)
        for i := range (arr) {
          // CODE
        }

    case []string :
        arr := (someArr).([]string)
        for i := range (arr) {
          // CODE
        }
  } 
}

在这个例子中,CODE部分的代码完全相同。然而,我不能将它从switch语句中取出,因为类型断言arr会超出作用域。同样,我也不能在switch之前定义arr,因为我不知道它的类型。可能这是无法实现的。在这种情况下,当我解析具有不规则模式的JSON(一些整数数组,一些字符串数组)时,有什么更好的惯用法吗?

英文:

Just getting started writing Go code and I ran into an interesting problem.

Is there a way to easily iterate over the items in an array that is brought in as an empty interface without code duplication? Consider the following:

function(someArr interface{}){
  switch someArr.(type){
    case []int :
        arr := (someArr).([]int)
        for i := range (arr) {
          // CODE
        }

    case []string :
        arr := (someArr).([]string)
        for i := range (arr) {
          // CODE
        }
  } 
}

In this example the code in CODE is exactly the same. However, I cannot take it out of the switch because the type assertion arr would fall out of scope. Similarly, I can't define arr before the switch because I don't know what type it will be. It's possible that this just cannot be done. In that case, what's a better idiom for this kind of thing when I'm, say, parsing JSON with an irregular schema (some arrays of ints, some arrays or strings)?

答案1

得分: 6

你可以使用reflect包来遍历任意切片。但是,显式地实现特殊情况(比如[]int)通常更快,并且通常会在避免常见情况下的反射之外进行。

package main

import "fmt"
import "reflect"

func foo(values interface{}) {
    rv := reflect.ValueOf(values)
    if rv.Kind() != reflect.Slice {
        return
    }
    n := rv.Len()
    for i := 0; i < n; i++ {
        value := rv.Index(i).Interface()
        fmt.Println(value)
    }
}

func main() {
    foo([]int{1, 3, 3, 7})
}

**编辑:**我不确定为什么有人对问题和我的回答进行了负投票,但是有些情况下你需要处理这样的代码。即使标准库中也包含了大量这样的代码,例如看看"fmt"、"gob"、"json"、"xml"和"template"。提问者可能面临类似的问题。

英文:

You can use the reflect package to iterate over arbitrary slices. But implementing the special cases (like []int) explicitly is generally faster and is often done in addition to avoid reflection in common cases.

package main

import &quot;fmt&quot;
import &quot;reflect&quot;

func foo(values interface{}) {
	rv := reflect.ValueOf(values)
	if rv.Kind() != reflect.Slice {
		return
	}
	n := rv.Len()
	for i := 0; i &lt; n; i++ {
		value := rv.Index(i).Interface()
		fmt.Println(value)
	}
}

func main() {
	foo([]int{1, 3, 3, 7})
}

Edit: I'm not sure why somebody has down voted the question and my answer, but there are cases where you need to deal with code like that. Even the standard library contains plenty of it, take a look at "fmt", "gob", "json", "xml" and "template" for example. The questioner might face a similar problem.

答案2

得分: 6

你的示例代码并不符合Go语言的惯用写法,尽管从词法上看,惯用写法似乎也违反了DRY原则。

关键要理解的是,x在每个类型分支中都是一个独立的、具有不同类型的变量:

function(someArr interface{}) {
    switch x := someArr.(type) {
    case []int:
        for i := range x {
            // 代码
        }
    case []string:
        for i := range x {
            // 代码
        }
    }
}
英文:

Your example is not idiomatic Go code, even though the idiomatic one lexically seems violating the DRY principle as well.

The key point to understand is that 'x' is a separate, differently typed variable in each type case:

function(someArr interface{}){
        switch x := someArr.(type) {
        case []int:
                for i := range x {
                        // CODE
                }
        case []string:
                for i := range x {
                        // CODE
                }
        }
}

huangapple
  • 本文由 发表于 2013年8月21日 04:05:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/18344063.html
匿名

发表评论

匿名网友

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

确定