我想使用接口(interface)交换两个数字,但接口(interface)的概念让我很困惑。
http://play.golang.org/p/qhwyxMRj-c
这是代码和 Playground 。如何使用界面并交换两个输入数字?我需要定义两个结构吗?
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{} 类型只是一种接受所有值的类型,因为它是一个具有空方法集的接口(interface),并且每种类型都可以满足这一点。例如,int 没有任何方法,interface{} 也没有。
对于交换两个变量值的方法,您首先需要确保这些变量实际上是可修改的。传递给函数的值总是被复制(除了像 slice 和映射这样的引用类型,但这不是我们目前关心的)。您可以通过使用指向变量的指针来实现可修改的参数。
因此,有了这些知识,您可以继续定义 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() 反射(reflect)了 a 和 b这样我们就可以
检查它。在同一行中,我们假设我们有指针值和取消引用
他们通过调用 .Elem()在他们身上。
这基本上转换为 ra := *a 和 rb := *b。
之后,我们通过使用 .Interface() 请求值来制作 *a 的副本。
并分配它(有效地制作副本)。
最后,我们使用[ra.Set(rb)] 5 将a 的值设置为b ,转换为 *a = *b
然后将 b 分配给我们存储在 temp 中的 a。变前。为了这,
我们需要将 tmp 转换回自身的反射,以便可以使用 rb.Set()
(它需要一个 reflect.Value 作为参数)。
是的!我们可以使代码更加类型安全,或者更好的是,使 Swap 的定义类型安全
通过使用 reflect.MakeFunc .在文档中(点击链接)是一个非常有用的例子
就像你正在尝试的那样。本质上你可以用内容填充函数原型(prototype)
通过使用反射。当您提供函数的原型(prototype)(签名)时
编译器可以检查类型,当值被缩减为 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 是一样的
作为文档中使用的那个,解释得很好。
免责声明:上面的代码对给定的内容做了很多假设,
值(value)观是什么样的。通常你需要检查,例如,给定的
SwapNum 的值实际上是指针值等等。我把它留给了
清晰的原因。
关于戈朗 : interface to swap two numbers,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19301742/
关闭。这个问题需要更多focused.它目前不接受答案。想改进这个问题吗?更新问题,使其只关注一个问题editingthispost.关闭3年前。Improvethisquestion告诉我如何使用Golang登录网站。下载xls文件是得到了,但是为了在Excel表格中有数据,需要登录网站。该站点位于公司的服务器上。如果你能告诉你怎么做。例如,我用来执行此操作的VBA代码。SetoFields=CreateObject("Scripting.Dictionary")WithoFields.Add"login","sdiscor".Add"password","sdiscor"EndWi
下面是我查询的代码:我有一个单维数组a当我打印a[0][0]时,我不明白为什么它返回字符a的ascii值:packagemainimport("fmt")funcmain(){a:=[3]string{"a","b","c"}fmt.Println(a[0][0])}输出:97 最佳答案 下面是如何打印ascii的代码示例a:=[3]string{"a","b","c"}for_,rune:=rangea{fmt.Println(rune)//Itwillprinta,b,c}因为你在你的代码中使用了[0][0],所以它是等价的fo
typepath[]bytefunc(ppath)ToUpper(){fori,b:=rangep{if'a'在上面(这个例子是从“TheGoBlog”复制过来的),如果ToUpper变成这样:func(ppath)ToUpper(){fori,_:=rangep{if'a'哪个会更有效率为什么?“TheGoBlog”对前一个说:“这里的ToUpper方法在forrange构造中使用两个变量来捕获索引和slice元素。这种形式的循环避免了在主体中多次写入p[i]。”什么意思? 最佳答案 前者有更多的内存操作,即在b上:它在循环的第一
我有一个用Golang编写的脚本,我不太明白。我想知道他为什么要在脚本里面写goserver.Start()?为什么不简单地编写server.Start?packagemainimport("github.com/miekg/dns""testing""time")constTEST_ADDR="127.0.0.1:9953"funcTestDNSResponse(t*testing.T){server:=NewDNSServer(&Config{dnsAddr:TEST_ADDR,})goserver.Start()//Allowsometimeforservertostarttim
我正在尝试在Go中做一些相对简单的事情——将字符串转换为整数,然后将其加倍:myInt,_:=strconv.Atoi(args[1])doubleArg:=myInt*2由于Atoi()返回两个参数(整数和err),我使用myInt,_:=来检索值的整数。我想将它加倍(因此是第二行)但不能在一行中完成所有操作:myInt,_:=strconv.Atoi(args[1])*2给我:multiple-valuestrconv.Atoi()insingle-valuecontext但是,根据我使用大多数其他语言的经验,必须在两行中执行此操作似乎有很多样板。这只是我必须处理的一个限制,还是有
我刚接触golang。尝试通过golang实现批量上传到Elasticsearch。我正在使用golang库->https://github.com/olivere/elastic用于与Elasticsearch通信。此外,我正在尝试一段示例代码,但出现以下错误...suresh@BLR-245:~/Desktop/tools/golang/src$goinstallgithub.com/crazyheart/elastic-bulk-upload#github.com/crazyheart/elastic-bulk-uploadgithub.com/crazyheart/elasti
我计划实现一个go-routine并有一个sync.WaitGroup同步创建的go-routine的结尾。我基本上使用go创建了一个线程.所以它是这样的:main(){varwgsync.WaitGroupfor{gomyThread(wg)wg.Add(1)}wg.wait()}myThread(wgsync.WaitGroup){deferwg.Done()}我之前曾与pthread_create合作过在某些情况下确实无法创建线程。在这种情况下,是否可能针对上述gomyThread(wg)无法启动和/或运行wg.Done()例程的其余部分是否正常运行?如果是这样,将报告什么以及如
Go的append()函数仅在给定slice的容量不足时分配新的slice数据(另请参见:https://stackoverflow.com/a/28143457/802833)。这可能会导致意外行为(至少对我这个golang新手来说):packagemainimport("fmt")funcmain(){a1:=make([][]int,3)a2:=make([][]int,3)b:=[][]int{{1,1,1},{2,2,2},{3,3,3}}common1:=make([]int,0)common2:=make([]int,0,12)//providesufficientcap
我需要编写一个测试来验证服务器响应。响应必须包含某些header和xml正文。首先,如何检查响应中是否存在所需的header。以及如何比较收到的XML和所需的XML。例如。响应必须包含header“Serv”。正文必须包含对象为“person”的xmlHTTP/1.1200OKConnection:Keep-AliveServ:"any-string"Content-Length:0Content-Type:text/xml;charset=UTF-8string-value我如何检查响应是否包含标题“Serv”并包含带有元素人和名称的正文xml 最佳答案
我四处搜索并没有找到另一个这样做的例子,但我无意中发现我能够通过简单地将另一个slice的片段传递给接受slice并返回它的函数来从另一个slice的片段创建一个sliceslice。例子:packagemainimport"fmt"funcmakeSliceFrom(s[]int)[]int{returns}funcmain(){s:=[]int{1,2,3,4,5,6,7,8,9,10}newS:=makeSliceFrom(s[1:7])fmt.Println(newS)}我不是在问这是否有效,因为我知道它有效并且似乎运作良好,我是在问这是否得到支持或有一些我不知道的不可预见的成