作者s25g5d4 (function(){})()
看板Programming
标题Re: [问题] Golang型态转换
时间Wed Feb 8 22:46:58 2023
※ 引述《umaka0325 (Umaka)》之铭言:
: 最近在摸索Go的相关语法碰到一些问题想请教一下
: 程式码如下:
: type Person struct {
: Name string
: Age int
: }
: func test1(p *Person) {
: p.Name = "123"
: }
: func test(p any) {
: test1(p.(*Person))
: ^^^^^^^^
: }
: 想请问底线部分的*Person这个结构有什麽办法从输入p any动态产生吗?
: 谢谢!!
初学者常常希望能存取不特定型别的共同栏位
不过...
https://tenor.com/bhDEJ.gif
正常的写法都是改写成 interface
type Person interface {
GetName() string
GetAge() int
}
type person struct {
Name string
Age int
}
func (p *person) GetName() string {
return p.Name
}
func (p *person) GetAge() int {
return p.Age
}
func test(p Person) {
otherfunc(p.GetName(), ...)
otherfunc(p.GetAge(), ...)
}
func callTest() {
pe := &person{
Name: "John Doe",
Age: 20,
}
test(pe)
}
对,golang 的 interface 就是如此繁琐
--
如果共通 type 只有几种 struct 会用到
可以独立出一个 base struct embed 进其他 struct
type Model struct {
ID string
}
type RecordA struct {
Model
Entry1 int
}
type RecordB struct {
Model
Entry2 int
}
func test(record *Model) {
otherfunc(record.ID, ...)
}
func callTest() {
a := &RecordA{
Model: Model{
ID: "hello",
},
Entry1: 0,
}
b := &RecordB{
Model: Model{
ID: "world",
},
Entry2: 1,
}
test(a.Model)
test(b.Model)
}
但是这种写法是比较不推荐的。
原因是易遭到滥用,很多人会把这种语法当作继承 (inheritance) 使用,
但 golang 没有继承,只有组合 (composition),所以他还是分开的两种 type。
当你的 type 会跨出这个 package 的时候建议避免采用这种方式。
当然,如果你知道自己在做什麽也不是不能用。
比较经典的范例是 gorm.Model
https://gorm.io/docs/models.html
或是常常会有人把 sync.Mutex 嵌进 struct 里。
初学者看完 Effective Go 建议可以参考
https://github.com/uber-go/guide/blob/master/style.md
当然这不代表所有业界程式码风格规范,
但这份文件好处是为什麽要这样做的理由都告诉你了。
里面有提到 Avoid Embedding Types in Public Structs,
给的理由是 These embedded types leak implementation details,
inhibit type evolution, and obscure documentation.
简单的说就是 exported fields & struct methods 会被一并公开,
以及修改被嵌入的 struct 会影响所有有嵌入它的 struct,
造成可能的同名 field 及 method 冲突,制造修改衍生 struct 的困扰。
--
那如果因为一些技术限制不能改写 interface 也不能用 embed struct 呢?
最後的大招是用 reflect,写起来太丑我不想写,
如果写到要用 reflect 那你有 99% 的可能写错了。
这边还是稍微提一下作法。
用 reflect 你可以判断传进来的是 slice, array, struct,
接着可以再去读它有的 field name,进而去存取该 field 的值。
为什麽这个写法常常是错的呢?因为它效能不太好。
在上述两种方法前,reflect 常常会是最终不得已采用的解法。
一般常见用到 reflect 的写法有... gorm... 又是你...
https://github.com/go-gorm/gorm/blob/v1.24.5/schema/schema.go#L79
因为 gorm 几乎所有操作都是用 interface{} 去接,
所以它只能长成 reflect 的那种样子。
可以的话用 interface, type assertion 去做,不要用 reflect。
除了效能会受影响外,它还很难读。
除此之外,还有些 assetion/mock libraries 会用到 reflect。
但那也是无可厚非,因为它不知道使用者会传什麽 type 进来,
也没办法用 interface 定义,不然为了写测试真的要万物 interface 了。
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 219.91.34.68 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/Programming/M.1675867654.A.09F.html
※ 编辑: s25g5d4 (219.91.34.68 台湾), 02/08/2023 22:51:15
※ 编辑: s25g5d4 (219.91.34.68 台湾), 02/08/2023 22:54:21
1F:→ s25g5d4: 写完才发现是去年 9 月的文... orz 219.91.34.68 02/08 22:58
2F:推 CoNsTaR: 赶快支援 dependent types 啊,就不用这 174.112.12.38 02/09 01:53
3F:→ CoNsTaR: 麽麻烦了 174.112.12.38 02/09 01:53
4F:推 zxcchiou: 推推 愿意给解法 60.250.229.175 02/21 11:08
5F:推 liu2007: 太麻烦了不想写,还是写一下好了,这是傲 49.217.197.84 03/03 10:34
6F:→ liu2007: 娇吗XD 49.217.197.84 03/03 10:34
7F:→ s25g5d4: 没有啊,我只有提作法没给范例 219.91.34.68 03/04 00:49