我想使用接口交换两个数字,但是接口概念令我感到困惑。
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 }
首先,interface{}类型只是接受所有值的类型,因为它是带有空方法集的接口,并且每种类型都可以满足该要求。int例如没有任何方法,也没有interface{}。
interface{}
int
对于交换两个变量的值的方法,首先需要确保这些变量实际上是可修改的。传递给函数的值始终会被复制(切片和映射之类的引用类型除外,但这不是我们当前关注的问题)。您可以通过使用指向变量的指针来实现可修改的参数。
因此,有了这些知识,您就可以SwapNum像下面这样定义:
SwapNum
func SwapNum(a interface{}, b interface{})
现在SwapNum是一个接受两个任何类型参数的函数。你不会写
func SwapNum(a *interface{}, b *interface{})
因为这将只接受类型的参数,*interface{}而不是任何类型的参数。(在这里自己尝试)。
*interface{}
因此,我们有一个签名,剩下的唯一事情就是交换值。
func SwapNum(a interface{}, b interface{}) { *a, *b = *b, *a }
不,这将 无法 正常工作。通过使用,interface{}我们必须执行运行时类型声明来检查我们是否在做正确的事情。因此,必须使用该reflect软件包扩展代码。如果您不了解反射,本文可能会让您入门。
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)) }
该代码反映a并b使用reflect.ValueOf()了我们可以对其进行检查的代码。在同一行中,我们假设我们有指针值并通过调用它们来取消引用.Elem()它们。
a
b
reflect.ValueOf()
.Elem()
这基本上转化为ra := *a和rb := *b。之后,我们*a通过使用.Interface() 并分配值(有效地制作副本)来请求的副本。
ra := *a
rb := *b
*a
.Interface()
最后,我们使用] 5设置ato 的值,该值转换为 然后分配给,我们将其存储在temp中。变量之前。为此,我们需要转换回自身的反射,以便可以使用它(将a 作为参数)。b``[ra.Set(rb)*a = *b``b``a``tmp``rb.Set()``reflect.Value
b``[ra.Set(rb)
*a = *b``b``a``tmp``rb.Set()``reflect.Value
是! 我们可以使用来使代码更安全,或者更好地使Swap类型的定义安全reflect.MakeFunc。在文档中(跟随链接)是一个示例,非常类似于您尝试的示例。本质上,您可以使用反射将内容填充到函数原型中。当您提供函数的原型(签名)时,编译器可以检查类型,将值减小为时无法检查类型interface{}。
Swap
reflect.MakeFunc
用法示例:
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与文档中使用的相同,说明得很好。
swap
makeSwap
免责声明: 上面的代码对给定的值和值的外观进行了许多假设。通常,例如,您需要检查给定的值SwapNum实际上是指针值,依此类推。为了清楚起见,我将其省略。