[更新了一個不那么做作的例子]
我正在嘗試擴展一個通用函式來為特定型別提供不同的行為。當我直接呼叫該函式時,這按預期作業。但是,如果我從另一個泛型函式中呼叫它,則不會保留原始泛型型別,并且會得到默認行為。我對 Swift 有點陌生,所以我可能在這里遺漏了一些明顯的東西。
我的代碼看起來像這樣:
protocol Task {
associatedtype Result
}
struct SpecificTask: Task {
typealias Result = String
// other taks related properties
}
enum TaskRunner<T: Task> {
static func run(task: T) throws -> T.Result {
// Task not supported
throw SomeError.error
}
}
extension TaskRunner where T == SpecificTask {
static func run(task: T) throws -> T.Result {
// execute a SpecificTask
return "Some Result"
}
}
func run<T: Task>(task: T) throws -> T.Result {
// Additional logic related to running the task
return try TaskRunner.run(task: task)
}
print(try TaskRunner.run(task: SpecificTask())) // Prints "Some Result"
print(try run(task: SpecificTask())) // Throws SomeError
我需要頂級 run 函式來呼叫低級 run() 函式的 specificTask 版本,但呼叫的是該函式的通用版本
uj5u.com熱心網友回復:
您正在嘗試使用泛型重新發明類繼承。那不是泛型的用途,它們不是那樣作業的。通用方法是靜態分派的,這意味著代碼是在編譯時而不是運行時選擇的。多載永遠不應該改變函式的行為(這就是你在這里嘗試做的)。where子句中的覆寫可用于提高性能,但不能用于創建動態(運行時)調度。
如果必須使用繼承,則必須使用類。也就是說,您描述的問題最好使用通用任務而不是協議來解決。例如:
struct Task<Result> {
let execute: () throws -> Result
}
enum TaskRunner {
static func run<Result>(task: Task<Result>) throws -> Result {
try task.execute()
}
}
let specificTask = Task(execute: { "Some Result" })
print(try TaskRunner.run(task: specificTask)) // Prints "Some Result"
請注意這如何消除“不支持任務”的情況。它現在不是運行時錯誤,而是編譯時錯誤。您不能再錯誤地呼叫它,因此您不必檢查這種情況。
如果您真的想要動態分派,這是可能的,但您必須將其實作為動態分派,而不是多載。
enum TaskRunner<T: Task> {
static func run(task: T) throws -> T.Result {
switch task {
case is SpecificTask:
// execute a SpecificTask
return "Some Result" as! T.Result // <=== This is very fragile
default:
throw SomeError.error
}
}
}
這是脆弱的,因為as! T.Result. 如果您將 SpecificTask 的結果型別更改為 String 以外的其他內容,它將崩潰。但重要的一點是case is SpecificTask,它是在運行時確定的(動態調度)。如果您需要task,并且我假設您確實需要,則可以將其與if let task = task as? SpecificTask.
在走這條路之前,我會重新考慮設計,看看這將如何真正被稱為。由于 Result 型別是通用的,您不能在回圈中呼叫任意任務(因為所有回傳值都必須匹配)。所以這讓我想知道什么樣的代碼實際上可以呼叫run.
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/363428.html
