簡體   English   中英

在帶有 Swift 的 function 中使用類型擦除返回通用類型(無法轉換類型的返回表達式......)

[英]Use Type Erasure return Generic Type in a function with Swift (Cannot convert return expression of type…)

我對 swift 中的 generics 有疑問。 讓我們公開我的代碼。


protocol FooProtocol {
    associatedtype T
}

protocol Fooable { }
extension Int : Fooable { }
extension String: Fooable { }

class AnyFoo<T>: FooProtocol {
    init<P: FooProtocol>(p: P) where P.T == T { }
}

class FooIntImpClass: FooProtocol {
    typealias T = Int
}

class FooStringImpClass: FooProtocol {
    typealias T = String
}

func createOne(isInt: Bool) -> AnyFoo<Fooable> {
    if isInt {
        let anyFoo = AnyFoo(p: FooIntImpClass())
          return anyFoo
    } else {
        let anyFoo = AnyFoo(p: FooStringImpClass())
        return anyFoo
    }
}

func createTwo<F: Fooable>(isInt: Bool) -> AnyFoo<F> {
    if isInt {
        let anyFoo = AnyFoo(p: FooIntImpClass())
          return anyFoo
    } else {
        let anyFoo = AnyFoo(p: FooStringImpClass())
        return anyFoo
    }
}

createOne出現錯誤

無法將類型“AnyFoo”(又名“AnyFoo”)的返回表達式轉換為返回類型“AnyFoo”

createTwo出錯

無法將類型“AnyFoo”(又名“AnyFoo”)的返回表達式轉換為返回類型“AnyFoo”

為什么會這樣。 我正在返回正確的值。

createOnecreateTwo有什么區別

編輯以回應對問題的編輯:

createTwo不起作用,因為您有與我在原始答案中所說的相同的誤解。 createTwo決定F應該是StringInt ,而不是“任何符合Fooable的類型”。

對於createOne ,您還有另一個常見的誤解。 泛型類是不變的 AnyFoo<String>不是AnyFoo<Fooable>的一種。 事實上,它們是完全不相關的類型! 有關更多詳細信息,請參見此處

基本上,您嘗試做的事情違反了類型安全,並且您重新設計了 API 並選擇了另一種不同的方法。


原始答案(用於問題的初始修訂)

您似乎對 generics 有一個常見的誤解。 通用參數由調用者決定,而不是被調用者。

createOne中,您將返回anyFoo ,它的類型是AnyFoo<Int> ,而不是AnyFoo<P> 方法(被調用者)自己決定P應該是Int 這不應該發生,因為調用者決定了泛型參數應該是什么。 如果被調用者是通用的,它必須能夠使用任何類型(在約束內)。 無論如何, P在這里無論如何都不能是Int ,因為P: FooProtocol

您的createOne方法根本不應該是通用的,因為它僅適用於Int

func createOne() -> AnyFoo<Int> {
    let anyFoo = AnyFoo(p: FooImpClass())
    return anyFoo
}

以下是您試圖實現的目標嗎? (使用 Xcode 11.4 編譯和測試)

func createOne() -> some FooProtocol {
    let anyFoo = AnyFoo(p: FooImpClass())
    return anyFoo
}

編輯我終於設法保留了你的where clause :)

編輯仍然不確定你想做什么,我仍然同意@Sweeper,但我喜歡嚴重濫用 generics :):


protocol FooProtocol {
    associatedtype T
    init()
}

protocol Fooable { }
extension Int : Fooable { }
extension String: Fooable { }

class AnyFoo<T>: FooProtocol {
    init<P: FooProtocol>(p: P) where P.T == T { }
    init<T>(p: T.Type) { }
    required init() { }
}

class FooIntImpClass: FooProtocol {
    typealias T = Int
    required init() { }
}

class FooStringImpClass: FooProtocol {
    typealias T = String
    required init() { }
}


func createOne<F: FooProtocol>(foo: F.Type) -> AnyFoo<F.T> {
    let anyFoo = AnyFoo<F.T>(p: F.init())
        return anyFoo
}

func createTwo<F: FooProtocol>(foo: F.Type) -> some FooProtocol {
    let anyFoo = AnyFoo<F.T>(pk: F.T.self)
    return anyFoo
}

編譯,但我不知道如何處理它。

編輯

是的,我真的不知道:

let one = createOne(foo: FooStringImpClass.self) // AnyFoo<String>
print(type(of: one).T) // "String\n"
let two = createTwo(foo: FooIntImpClass.self) // AnyFoo<Int>
print(type(of: two).T) // "Int\n"

那是你想要的嗎?


我不確定你想用它做什么,但我建議你嘗試在AnyFoo class 上放置一個where子句,而不是它的初始化程序。 我應該補充一點,你在初始化器上的 where 子句是錯誤的,就像Sweeper說的:

無論如何,P 在這里無論如何都不能是 Int,因為 P: FooProtocol.

以下代碼編譯:

 protocol FooProtocol { associatedtype T } class AnyFoo<T>: FooProtocol where T: FooProtocol { init<P: FooProtocol>(p: P) { } } class FooImpClass: FooProtocol { typealias T = Int } func createOne<P: FooProtocol>() -> AnyFoo<P> { let anyFoo: AnyFoo<P> = AnyFoo(p: FooImpClass()) return anyFoo }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM