Golang:交换两个数字的接口

时间:2013-10-10 16:54:16

标签: go

我想使用界面交换两个数字,但界面概念对我来说太混乱了。

http://play.golang.org/p/qhwyxMRj-c

这是代码和游乐场。如何使用界面和交换两个输入数字?我需要定义两个结构吗?

type num struct {
    value interface{}
}

type numbers struct {
    b *num
    c *num
}

func (a *num) SwapNum(var1, var2 interface{}) {
    var a num

    temp := var1
    var1 = var2
    var2 = temp
}

func main() {
    a := 1
    b := 2
    c := 3.5
    d := 5.5

    SwapNum(a, b)
    fmt.Println(a, b) // 2 1
    SwapNum(c, d)
    fmt.Println(c, d) // 5.5 3.5
}

1 个答案:

答案 0 :(得分:15)

首先,interface{}类型只是一个接受所有值的类型,因为它是一个具有空方法集的接口,并且每个类型都可以满足该类型。 int例如没有任何方法,interface{}也没有。

对于交换两个变量值的方法,首先需要确保这些变量实际上是可修改的。传递给函数的值总是被复制(除了切片和贴图之类的引用类型,但目前我们不关心这一点)。您可以通过使用指向变量的指针来实现可修改的参数。

因此,凭借这些知识,您可以继续并定义SwapNum,如下所示:

func SwapNum(a interface{}, b interface{})

现在SwapNum是一个接受任何类型的两个参数的函数。 你不能写

func SwapNum(a *interface{}, b *interface{})

因为这只接受*interface{}类型的参数,而不仅仅是任何类型。 (亲自试试here)。

所以我们有一个签名,唯一剩下的就是交换价值。

func SwapNum(a interface{}, b interface{}) {
    *a, *b = *b, *a
}

不,这将以这种方式工作。通过使用interface{},我们必须执行运行时类型断言来检查我们是否正在做正确的事情。因此必须使用reflect包扩展代码。如果您不了解反思,This article可能会帮助您。

基本上我们需要这个功能:

func SwapNum(a interface{}, b interface{}) {
    ra := reflect.ValueOf(a).Elem()
    rb := reflect.ValueOf(b).Elem()
    tmp := ra.Interface()

    ra.Set(rb)
    rb.Set(reflect.ValueOf(tmp))
}

此代码使用reflect.ValueOf()反映ab,以便我们可以 检查它。在同一行中,我们假设我们有指针值和解引用 他们通过调用.Elem()就可以了。

这基本上转换为ra := *arb := *b。 之后,我们通过使用.Interface()请求值来复制*a 并指定它(有效地制作副本)。

最后,我们使用a] 5b的值设置为[ra.Set(rb),转换为*a = *b 然后将b分配给我们存储在temp中的a。变量之前。为了这, 我们需要将tmp转换回自身的反映,以便可以使用rb.Set() (它需要reflect.Value作为参数)。

我们可以做得更好吗?

是的!我们可以使代码更安全,或更好,使Swap类型的定义安全 使用reflect.MakeFunc。在文档中(按照链接)是一个非常好的例子 喜欢你正在尝试的东西。基本上你可以用内容填充函数原型 通过使用反射。当你提供函数的原型(签名)时 编译器可以检查类型,当值减少到interface{}时,它不能。

使用示例:

var intSwap func(*int, *int)
a,b := 1, 0
makeSwap(&intSwap)
intSwap(&a, &b)
// a is now 0, b is now 1

这背后的代码:

swap := func(in []reflect.Value) []reflect.Value {
    ra := in[0].Elem()
    rb := in[1].Elem()
    tmp := ra.Interface()

    ra.Set(rb)
    rb.Set(reflect.ValueOf(tmp))

    return nil
}

makeSwap := func(fptr interface{}) {
    fn := reflect.ValueOf(fptr).Elem()
    v := reflect.MakeFunc(fn.Type(), swap)
    fn.Set(v)
}

swap的代码与SwapNum的代码基本相同。 makeSwap是一样的 正如在文档中使用的那个很好地解释它。

免责声明:上面的代码对给出的内容做了很多假设 价值观是什么样的。通常,您需要检查,例如,给定的 值SwapNum实际上是指针值等等。我把它留给了 清晰的原因。