2 回答

TA貢獻(xiàn)1853條經(jīng)驗(yàn) 獲得超6個(gè)贊
為什么泛型類型開(kāi)關(guān)編譯失???
這其實(shí)是 Go 團(tuán)隊(duì)有意決定的結(jié)果。事實(shí)證明,允許參數(shù)化類型上的類型切換會(huì)導(dǎo)致混淆
在此設(shè)計(jì)的早期版本中,我們?cè)试S在類型為類型參數(shù)或類型基于類型參數(shù)的變量上使用類型斷言和類型開(kāi)關(guān)。我們刪除了此功能,因?yàn)槭冀K可以將任何類型的值轉(zhuǎn)換為空接口類型,然后對(duì)其使用類型斷言或類型切換。此外,有時(shí)令人困惑的是,在使用近似元素的類型集的約束中,類型斷言或類型切換將使用實(shí)際類型參數(shù),而不是類型參數(shù)的基礎(chǔ)類型(差異在識(shí)別部分中解釋)匹配的預(yù)聲明類型)
來(lái)自類型參數(shù)提案
讓我把強(qiáng)調(diào)的語(yǔ)句變成代碼。如果類型約束使用類型近似(注意波浪號(hào))...
func PrintStringOrInt[T ~string | ~int](v T)
...如果還有一個(gè)自定義類型int
作為基礎(chǔ)類型...
type Seconds int
...如果PrintOrString()
使用Seconds
參數(shù)調(diào)用...
PrintStringOrInt(Seconds(42))
...那么switch
塊不會(huì)進(jìn)入,int case
而是直接進(jìn)入default case
,因?yàn)?code>Seconds不是int
。開(kāi)發(fā)人員可能希望它也case int:
與類型相匹配Seconds
。
要允許一個(gè)case
語(yǔ)句同時(shí)匹配Seconds
和int
將需要一個(gè)新的語(yǔ)法,例如,
case ~int:
在撰寫本文時(shí),討論仍在進(jìn)行中,也許會(huì)產(chǎn)生一個(gè)全新的選項(xiàng)來(lái)打開(kāi)類型參數(shù)(例如switch type T
)。
更多細(xì)節(jié)請(qǐng)參考提案:spec: generics: type switch on parametric types
技巧:將類型轉(zhuǎn)換為“任何”
幸運(yùn)的是,我們不需要等待這個(gè)提議在未來(lái)的版本中得到實(shí)施?,F(xiàn)在有一個(gè)超級(jí)簡(jiǎn)單的解決方法。
不是打開(kāi)v.(type)
,而是打開(kāi)any(v).(type)
。
switch any(v).(type) { ...
這個(gè)技巧轉(zhuǎn)換v
成一個(gè)空的interface{}
(又名any
),很switch
高興地為它進(jìn)行類型匹配。

TA貢獻(xiàn)1820條經(jīng)驗(yàn) 獲得超10個(gè)贊
*A[any]不匹配*A[int],因?yàn)閍ny是靜態(tài)類型,不是通配符。因此,用不同類型實(shí)例化一個(gè)泛型結(jié)構(gòu)會(huì)產(chǎn)生不同的類型。
為了在類型轉(zhuǎn)換中正確匹配泛型結(jié)構(gòu),您必須使用類型參數(shù)實(shí)例化它:
func Handle[T any](s interface{}) {
switch x := s.(type) {
case *A[T]:
x.DoA()
case *B[T]:
x.DoB()
default:
panic("no match")
}
}
盡管在沒(méi)有其他函數(shù)參數(shù)的情況下 infer T,您將不得不調(diào)用Handle顯式實(shí)例化。T不會(huì)僅從結(jié)構(gòu)中推斷出來(lái)。
func main() {
i := &A[int]{}
Handle[int](i) // expected to print "do A"
}
游樂(lè)場(chǎng):https ://go.dev/play/p/2e5E9LSWPmk
但是,當(dāng)Handle實(shí)際上是一種方法時(shí),就像在您的數(shù)據(jù)庫(kù)代碼中一樣,這具有在實(shí)例化接收器時(shí)選擇類型參數(shù)的缺點(diǎn)。
為了改進(jìn)這里的代碼,您可以創(chuàng)建Handle一個(gè)頂級(jí)函數(shù):
func Handle[T any](ctx context.Context, cmd CMD) error {
switch t := cmd.(type) {
case *TransactionalCommand[T]:
return handleTransactionalCommand(ctx, t)
default:
fmt.Printf("%T\n", cmd)
return ErrInvalidCommand
}
}
那么你就會(huì)遇到如何向db T命令函數(shù)提供參數(shù)的問(wèn)題。為此,您可能會(huì):
只需將一個(gè)附加*db參數(shù)傳遞給Handleand handleTransactionalCommand,這也有助于類型參數(shù)推斷。調(diào)用為Handle(ctx, &db{}, tFn)。游樂(lè)場(chǎng):https ://go.dev/play/p/6WESb86KN5D
傳遞一個(gè)實(shí)例CommandManager(如上面的解決方案但*db被包裝)。更冗長(zhǎng),因?yàn)樗枰谌魏蔚胤斤@式實(shí)例化。游樂(lè)場(chǎng):https ://go.dev/play/p/SpXczsUM5aW
改用參數(shù)化接口(如下所示)。因此,您甚至不必進(jìn)行類型切換。游樂(lè)場(chǎng):https ://go.dev/play/p/EgULEIL6AV5
type CMD[T any] interface {
Exec(ctx context.Context, db T) error
}
- 2 回答
- 0 關(guān)注
- 170 瀏覽
添加回答
舉報(bào)