Golang新手提问:如何通过函数传递可变结构体
Golang新手提问:如何通过函数传递可变结构体 大家好,
我很久没有接触实际编程了,现在刚开始学习 Go。我想创建一个函数,传入一个整数,然后根据这个整数返回不同的结构体。我不确定这是否可行,也尝试过返回指针,但还是不行。这个函数还出现了一个奇怪的编译错误,我不明白为什么会失败。
这个函数的调用方式如下:
csvdata := csvinit(cmds.InPrecision)
函数 ‘csvinit’ 的内容在: https://play.golang.org/p/_bQjKvnR9-n
我只需要一点提示,告诉我发生了什么以及如何实现我的需求。用函数来实现这个功能是正确的做法吗?我应该使用方法或接口来实现吗?
谢谢!
更多关于Golang新手提问:如何通过函数传递可变结构体的实战教程也可以访问 https://www.itying.com/category-94-b0.html
您想要的方式无法实现,需要为两个结构体实现一个公共接口,然后使用该接口类型作为返回类型。
更多关于Golang新手提问:如何通过函数传递可变结构体的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
虽然这是正确的,但这里的问题不在这里。结构体被读取了,只是标签没有被读取。
我正在尝试避免使用接口和方法,以保持简单性,因为会有几十种不同的结构体。
是否可以通过转换来实现?
也就是说,将空的 ‘csvdata’ 结构体转换为其中一个已填充的结构体?
你可以使用空接口并配合类型保护。但老实说,你真的不应该这样做。最终代码会变得难以维护。
好的,现在可以正常工作了: https://play.golang.org/p/FmwxItE1Bl4
但它会丢弃所有标签。所以现在又出现了另一个问题。😭😭
没有任何问题,但是反射无法查看私有字段。当你使用大写的字段标识符导出它们时,它就能正常工作了。
没有任何内容被丢弃,完整结果符合预期,这是该结构体的零值。
如果你为处理每种类型创建一个通用接口或完全独立的代码路径,你会遇到更少的麻烦,与语言的对抗也会大大减少。
Koguma:
你指的是哪个大写的字段标识符?
我指的是你结构体中的字段标识符。你使用了小写字母命名,因此包外部的代码无法看到这些字段。你需要将它们改为大写字母开头,才能使其可见并可访问。
我可能需要澄清一下。我确实让它工作了,但gocsv包丢失了结构体标签。是gocsv包有问题:https://github.com/gocarina/gocsv/issues/89
你指的是哪个大写的字段标识符?
是的,这很相似,但大写的字段名并没有起到作用,这个包存在一个bug。
不管怎样,我直接使用了该包中的CSVReader方法,放弃了使用它们的编组方法和结构体。虽然这样不太优雅,但实际上事情反而变得更简单了。
你的意思是当CSV函数使用结构体时,这些字段会被忽略吗?我没有使用Go读取CSV文件的经验,但如果它的工作方式与JSON类似,你必须使用大写的字段名,这样它们才能被导出,其他包才能读取它们。
感谢Nobbz!我遇到的部分问题似乎是我一直在使用的gocsv包中的反射机制有问题。它无法从接口反射回结构体。不过这确实是个很好的学习经历。
我会考虑重写代码,因为我要放弃使用gocsv包了… 😛
我发现了你的示例与包说明文档中示例的一个区别。在他们的函数调用中,他们传递了一个指针切片,而你只传入了一个结构体。这可能是问题所在。
clients := []*Client{}
if err := gocsv.UnmarshalFile(clientsFile, &clients); err != nil { // Load clients from file
panic(err)
}
然后我阅读了代码,发现Unmarshal函数使用的readTo函数首先会查找数组或切片的基础类型。
是的,我想避免那样做。
我尝试过转换,但出现了错误。
似乎也不能直接将一个全局结构体的指针赋值给另一个。
类似这样的写法 *csvdata{} := *CSVdataDB1{} 是行不通的。
我原本以为转换会有效,类似这样:
package main
import "fmt"
type CSVdataDB1 struct {
Name string
Age int
}
type csvdata struct {
Name string
Age int
}
func main() {
db1 := CSVdataDB1{Name: "John", Age: 30}
var data csvdata
data = csvdata(db1) // 尝试转换
fmt.Println(data)
}
那么,使用变量的内容来初始化结构体呢?有没有办法做到类似这样:
package main
import "fmt"
type CSVdataDB1 struct {
Name string
Age int
}
type csvdata struct {
Name string
Age int
}
func main() {
db1 := CSVdataDB1{Name: "John", Age: 30}
// 尝试使用 db1 的内容来初始化 csvdata 结构体
data := csvdata{Name: db1.Name, Age: db1.Age}
fmt.Println(data)
}
难道没有简单的方法来实现吗?
在Go语言中,通过函数返回不同类型的结构体是可行的,但需要处理类型系统约束。你的代码问题在于返回类型声明为 *csvData,但实际返回了不同类型的结构体指针,导致类型不匹配错误。
以下是解决方案和示例代码:
方法一:使用接口(推荐)
定义接口,让不同结构体实现相同的方法:
type CSVData interface {
GetHeaders() []string
GetData() [][]string
}
type csvDataPrecise struct {
Headers []string
Data [][]string
}
type csvDataSimple struct {
Headers []string
Data [][]string
}
func (c *csvDataPrecise) GetHeaders() []string {
return c.Headers
}
func (c *csvDataPrecise) GetData() [][]string {
return c.Data
}
func (c *csvDataSimple) GetHeaders() []string {
return c.Headers
}
func (c *csvDataSimple) GetData() [][]string {
return c.Data
}
func csvinit(precision int) CSVData {
if precision == 1 {
return &csvDataPrecise{
Headers: []string{"Time", "Value"},
Data: [][]string{},
}
}
return &csvDataSimple{
Headers: []string{"Timestamp", "Data"},
Data: [][]string{},
}
}
使用方式:
csvdata := csvinit(cmds.InPrecision)
headers := csvdata.GetHeaders()
data := csvdata.GetData()
方法二:返回空接口配合类型断言
func csvinit(precision int) interface{} {
if precision == 1 {
return &csvDataPrecise{
Headers: []string{"Time", "Value"},
Data: [][]string{},
}
}
return &csvDataSimple{
Headers: []string{"Timestamp", "Data"},
Data: [][]string{},
}
}
// 使用时需要类型断言
csvdata := csvinit(cmds.InPrecision)
if precise, ok := csvdata.(*csvDataPrecise); ok {
// 使用 precise
} else if simple, ok := csvdata.(*csvDataSimple); ok {
// 使用 simple
}
方法三:使用相同结构体,通过配置区分
type csvData struct {
Headers []string
Data [][]string
IsPrecise bool
}
func csvinit(precision int) *csvData {
if precision == 1 {
return &csvData{
Headers: []string{"Time", "Value"},
Data: [][]string{},
IsPrecise: true,
}
}
return &csvData{
Headers: []string{"Timestamp", "Data"},
Data: [][]string{},
IsPrecise: false,
}
}
接口方法是实现这种需求的最佳实践,它提供了类型安全和清晰的抽象。函数是实现此功能的正确方式,不需要使用方法,除非这些结构体有相关的接收者方法。

