在Golang中通過引用傳遞和值傳遞

在Golang中通過引用傳遞和值傳遞

有兩種方法可以將參數傳遞給函數。一個按值傳遞,另一個按引用傳遞。這兩種方式都有不同的概念,有時會給程序員帶來困惑。

簡單來說,傳遞值是指我們傳遞參數時沒有指向該值的原始地址的指針。並且通過引用傳遞是當我們使用指向給定參數的指針傳遞參數時。

在Golang中,我們可以在下面的示例中看到這兩個。

func passByValue(item string){} 
func passByReference(item * string){}

在Golang中通過引用傳遞和值傳遞

一個簡單得例子:

package main
import (
"fmt"
)
func main() {
item := ""
passByValue(item)
fmt.Println(item)
passByReference(&item)
fmt.Println(item)
}
func passByValue(item string) {
item = "hello"
}
func passByReference(item *string) {
*item = "world"

}

上面的示例顯示了我們可以根據類型(通過引用或值)傳遞參數的典型方法。

Golang中的接口參數傳遞參考

但是,有一天。我遇到了一個問題,我需要解決有關通過引用傳遞參數的問題。不像我曾經做過的任何普通的,這個在參數中使用接口。所以基本上,這個函數接受任何內容interface{},並根據該函數內部發生的邏輯填充值。

該功能如下所示。

func doSomethinWithThisParam(item interface {}){}

這個函數很簡單,它只接受一個接口,並在其中做一些事情。它不會返回任何錯誤,因此它只是用項中的值來水合項目。

因此,為了解決這種奇怪的行為,我試圖通過自己嘗試來解決。下面我將解釋我如何解決它的步驟。

第一次嘗試:指向接口的指針

package main
import (
"fmt"

)
func main() {
var item Student
doSomethinWithThisParam(&item)
fmt.Printf("%+v", item)
}
type Student struct {
ID string
Name string
}
func doSomethinWithThisParam(item *interface{}) {
*item = &Student{
ID: "124",
Name: "Iman Tumorang",
}
}

這一個無法編譯,它拋出錯誤。

cannot use &item (type *Student) as type *interface {} in argument to doSomethinWithThisParam: *interface {} is pointer to interface, not interface

第二次嘗試:直接為接口分配值

我嘗試沒有指向接口的指針,但相反,我將值直接分配給給定的參數。

func doSomethinWithThisParam(item interface{}) {
item = &Student{
ID: "124",
Name: "Iman Tumorang",
}
}
// Print: {ID: Name:}

打印完數據後,它仍無法正常工作。它打印空值。

第三次嘗試:轉換為原始類型並分配值

後來,在嘗試了很多東西后,我發現了一個有用的東西。該參數仍然是一個接口,但不是直接賦值,而是首先我必須將其強制轉換為原始類型。這時,它有點棘手。我們必須小心如何使用它。請參閱下面的區別。

下面這個例子沒有用。

func doSomethinWithThisParam(item interface{}) {
origin := item.(*Student)
origin = &Student{
ID: "124",
Name: "Iman Tumorang",
}
item = origin
}
// Print: {ID: Name:}

但是這就正常了。

func doSomethinWithThisParam(item interface{}) {
origin := item.(*Student)
origin.Name = "Iman Tumorang"
origin.ID = "124"
}
// Print: {ID:124 Name:Iman Tumorang}

What?????

起初,我有點困惑。這裡到底發生了什麼?怎麼可能在我這樣做的時候它不起作用。

origin := item.(*Student)
origin = &Student{
ID: "124",

Name: "Iman Tumorang",
}

但有了這個,它起作用了。

origin := item.(*Student)
origin.Name = "Iman Tumorang"

需要幾分鐘來解決這個問題。但後來我明白為什麼會這樣。

在解決問題後,我意識到了一些事情。第一個失敗,因為它取代了地址。因此,為了替換地址,我嘗試了一種只改變價值的新方法。

func doSomethinWithThisParam(item interface{}) {
origin := item.(*Student)
*origin = Student{
ID: "124",
Name: "Iman Tumorang",
}
item = origin
}
// Print: {ID:124 Name:Iman Tumorang}

最終的解析器

因此,經過多次試驗的試驗,最後我選擇了最後一個。並且因為我正在處理的這個函數將根據我當前的任務進行一些通用,我將其轉換並添加開關案例條件,以便根據我製作的開關案例更加通用。

簡單來說,我在當前任務中的所有工作都可以在下面的例子中描述。有一個通用函數可以接受接口並在其中做一些事情。它支持許多結構。

代碼段如下:

package main
\t
\timport (
\t\t"fmt"
\t)
\t
\tfunc main() {
\t\tvar item Student
\t\tdoSomethinWithThisParam(&item)
\t\tfmt.Printf("%+v", item)
\t}
\t
\ttype Student struct {
\t\tID string
\t\tName string
\t}
\t
\tfunc doSomethinWithThisParam(item interface{}) {
\t\tswitch v := item.(type) {
\t\tcase *Student:
\t\t\t*v = Student{
\t\t\t\tID: "124",
\t\t\t\tName: "Iman Tumorang",
\t\t\t}
\t\t\t// another case
\t\t}
\t}

結論

這個在Golang真的是非常嚴肅的事情。使用pass-by-reference和interface {}時,我們必須非常小心。為了避免任何不必要的錯誤,我建議為每個使用pass-by-reference方法的函數添加單元測試。

說實話,我在這個問題上被困了一個小時。因此,如果您認為這是一件好事,請分享這篇文章,這樣任何人都不會遇到同樣的問題。


分享到:


相關文章: