一尘不染

Golang:交换两个数字的接口

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
}

阅读 262

收藏
2020-07-02

共1个答案

一尘不染

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

对于交换两个变量的值的方法,首先需要确保这些变量实际上是可修改的。传递给函数的值始终会被复制(切片和映射之类的引用类型除外,但这不是我们当前关注的问题)。您可以通过使用指向变量的指针来实现可修改的参数。

因此,有了这些知识,您就可以SwapNum像下面这样定义:

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

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

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

因为这将只接受类型的参数,*interface{}而不是任何类型的参数。(在这里自己尝试)。

因此,我们有一个签名,剩下的唯一事情就是交换值。

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

不,这将 无法
正常工作。通过使用,interface{}我们必须执行运行时类型声明来检查我们是否在做正确的事情。因此,必须使用该reflect软件包扩展代码。如果您不了解反射,本文可能会让您入门。

基本上,我们将需要以下功能:

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

该代码反映ab使用reflect.ValueOf()了我们可以对其进行检查的代码。在同一行中,我们假设我们有指针值并通过调用它们来取消引用.Elem()它们。

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

最后,我们使用] 5设置ato 的值,该值转换为
然后分配给,我们将其存储在temp中。变量之前。为此,我们需要转换回自身的反射,以便可以使用它(将a
作为参数)。b``[ra.Set(rb)*a = *b``b``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基本相同SwapNummakeSwap与文档中使用的相同,说明得很好。

免责声明:
上面的代码对给定的值和值的外观进行了许多假设。通常,例如,您需要检查给定的值SwapNum实际上是指针值,依此类推。为了清楚起见,我将其省略。

2020-07-02