[英]Overloading generic functions in iOS Swift
我正在嘗試構建一個查找器,該查找器嘗試並找到在閉包內傳遞給它的多種類型。
enum SomeError: Error {
case notInitialized
}
struct TestFinder {
func getSomething<T, U>(_ function: @escaping (T) -> U) throws -> U {
guard let t: T = get() else {
throw SomeError.notInitialized
}
return function(t)
}
func getSomething<T, U, V>(_ function: @escaping (T, U) -> V) throws -> V {
guard let t: T = get(), let u: U = get() else {
throw SomeError.notInitialized
}
return function(t, u)
}
func getSomething<T, U, V, W>(_ function: @escaping (T, U, V) -> W) throws -> W {
guard let t: T = get(), let u: U = get(), let v: V = get() else {
throw SomeError.notInitialized
}
return function(t, u, v)
}
private func get<T>() -> T? {
nil
}
}
struct UserDetails {
let name: String
let roll: String
}
我稱取景器為:
let testReturnType = try? TestFinder().getSomething(UserDetails.init)
編譯器給我一個錯誤:
'getSomething' 的模糊使用
此錯誤的原因(來自文檔):
您可以通過在類型參數上提供不同的約束、要求或兩者來重載通用 function 或初始化程序。 當您調用重載的泛型 function 或初始化程序時,編譯器使用這些約束來解析要調用的重載 function 或初始化程序。
但如果我評論:
func getSomething<T, U>(_ function: @escaping (T) -> U) throws -> U
一切都開始工作。 它與編譯器無法識別要解析的 function 簽名有關。
對此有什么特別的解決方案嗎?
您還沒有完全關注實際問題。 讓我們消除與示例無關的所有內容。 這可以按預期編譯和工作:
struct TestFinder {
func doSomething<T,U>(_ function: (T,U) -> Void) -> Void {
print("two")
}
func doSomething<T,U,V>(_ function: (T,U,V) -> Void) -> Void {
print("three")
}
func doSomething<T,U,V,W>(_ function: (T,U,V,W) -> Void) -> Void {
print("four")
}
}
在這里我們將對其進行測試:
func f(_ s1: String, _ s2: String, _ s3: String, _ s4: String) -> Void {}
TestFinder().doSomething(f) // "four"
但是如果你添加一個傳遞了 function 參數的版本,一切都會崩潰:
struct TestFinder {
func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
}
func doSomething<T,U>(_ function: (T,U) -> Void) -> Void {
print("two")
}
func doSomething<T,U,V>(_ function: (T,U,V) -> Void) -> Void {
print("three")
}
func doSomething<T,U,V,W>(_ function: (T,U,V,W) -> Void) -> Void {
print("four")
}
}
現在我們無法編譯,因為第一個版本被視為候選版本。 事實上,如果我們刪除其他版本,我們仍然可以編譯!
struct TestFinder {
func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
}
}
這就是奇怪的部分。 我們仍然編譯,即使我們說:
func f(_ s1: String, _ s2: String, _ s3: String, _ s4: String) -> Void {}
TestFinder().doSomething(f)
顯然,這個帶有四個參數的 function 被編譯器視為只用一個通用參數“擬合”了聲明。
我認為這是一個錯誤。 我想我可以猜到可能導致它的原因; 它可能與 function 參數列表作為元組的遺留問題有關。 此 function f
與采用由四字符串元組組成的單個參數的 function “等效”。 盡管如此,您實際上不能在doSomething
中使用四字符串元組調用function
; 我根本找不到調用它的方法。
所以,我想說,把它當作一個錯誤,現在通過刪除你的泛型的第一個版本來解決它。
更新:根據 Swift 團隊的建議,我使用 2020 年 5 月 4 日的 Swift 5.3 開發工具鏈進行了測試。 有了它,您的代碼將按預期進行編譯和運行。 這確實是一個錯誤,它已作為
https://bugs.swift.org/browse/SR-8563
回到我的版本,我的代碼也按預期編譯和運行,所有四個版本的doSomething
都存在。 但是,請注意,如果您刪除除了第一個版本的doSomething
之外的所有內容,它仍然可以編譯並運行。 此外,您可以使用四個參數調用function
,方法是將它們捆綁到一個元組中並強制轉換,如下所示:
struct TestFinder2 {
func doSomething<T>(_ function: (T) -> Void) -> Void {
print("one")
function(("manny", "moe", "jack", "henry") as! T)
}
}
這似乎證實了我的猜測,即您所看到的是函數參數列表的隱藏元組性質的結果。 人們可以從對 bug 的討論中得出相同的結論,即“tuple-splatting”。
關鍵是 UserDetails 結構體,因為這個結構體有兩個屬性並且沒有任何設計的初始化器,初始化器可以是 UserDetails(name: , roll: ) 或 UserDetails(name: ) 或 UserDetails(roll: ),這是雄心勃勃的部分. 如果您只是刪除 UserDetails 的一個也可以使用的屬性,因為一個屬性結構只有一個設計的初始值設定項。
如果你評論
func getSomething<T, U>(_ function: @escaping (T) -> U) throws -> U
這意味着查找器只有一個選擇:
func getSomething<T,U,V>(_ function: @escaping(T,U) -> V) throws -> V
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.