簡體   English   中英

從使用的類型之一推斷通用類型 generics

[英]Infering Generic types from one of used types generics

我的應用程序中有一個非常復雜的Generic types結構。 這可行,但有一個問題,在這個鏈的末尾,我需要指定一些類型 2 次,因為它們需要用作一些 class 的 generics,而這些泛型類型之一,也需要泛型類型。 它們總是與之前的類型相同。 它是這樣<A, B, C<A, B>>

這使它使用起來有點不愉快。 有沒有辦法讓它從C推斷出AB這是示例代碼,具有剝離的功能:

// MARK: - Base classes that Im using, stripped from funcionalities.

// This one is a base for performing some detection. It can return any type as a result of scanning.
class DetectionPerformer<ResultType> {}

// This one adds possibility to load some model needed to perform scanning from the disk.
class LocalFileDetectionPerformer<ResultType, LocalModelType>: DetectionPerformer<ResultType> {
    required init(localModelURL: URL) {}
}

// This one adds possibility to download this model and store it on the disk before loading.
class DownloadableDetectionPerformer<ResultType, LocalModelType>: LocalFileDetectionPerformer<ResultType, LocalModelType> {}

// This one wraps LocalFileDetectionPerformer inside DownloadableDetectionPerformer, and use them together.
class RemoteFileDetectionPerformer<ResultType, LocalModelType, LocalFileDetectionPerformerType: DownloadableDetectionPerformer<ResultType, LocalModelType>>: DetectionPerformer<ResultType> {
    
    private let localFileDetectionPerformer: LocalFileDetectionPerformerType
    
    init(remoteModelURL: URL) {
        let localModelURL = Self.localModelURL(for: remoteModelURL)
        localFileDetectionPerformer = LocalFileDetectionPerformerType(localModelURL: localModelURL)
    }
    
    static func localModelURL(for url: URL) -> URL {
        url.appendingPathExtension("local")
    }
}

// Detector is main object in application. It takes some type of Detector as init parameter, and works on it.
class Detector<ResultType, DetectionPerformerType: DetectionPerformer<ResultType>> {
    let performer: DetectionPerformerType

    init(performer: DetectionPerformerType) {
        self.performer = performer
    }
}

// Now I can implement some specific performers, whcich will do real work. For example:
class SamplePerformer: DownloadableDetectionPerformer<Int, String> {}

// And I'm able to create Detector with any type of Performer:

let detectorA = Detector(performer: SamplePerformer(localModelURL: URL(string: "")!))

// The problem begins, when I want to wrap Performer into RemoteFileDetectionPerformer

let detectorB = Detector(performer: RemoteFileDetectionPerformer<Int, String, SamplePerformer>(remoteModelURL: URL(string: "")!))

// Here I need to specify all 3 generic types of RemoteFileDetectionPerformer, even tough two first are always the same as generic types of SamplePerformer. I can't even specify different ones, as this would create an error.
// Is there some way for RemoteFileDetectionPerformer to infer these first two generic types from LocalFileDetectionPerformerType? Maybe I need to construct these some differently?

我覺得您在代碼塊的前半部分顯示的類應該是協議。 也就是說, DetectionPerformerLocalFileDetectionPerformerDownloadableDetectionPerformer都應該是協議。 它們似乎沒有任何真正的實現,正如您的評論“現在我可以實現一些特定的表演者,這將做真正的工作”中顯而易見的那樣。 如果您有任何想要放入其中的實現,大多數時候您可以將其放入擴展中。 為什么使它們成為協議可以解決問題? 因為那時我們可以使用關聯類型而不是類型參數。

protocol DetectionPerformer {
    associatedtype ResultType
}

// This one adds possibility to load some model needed to perform scanning from the disk.
protocol LocalFileDetectionPerformer: DetectionPerformer {
    associatedtype LocalModelType
    init(localModelURL: URL)
}

// This one adds possibility to download this model and store it on the disk before loading.
protocol DownloadableDetectionPerformer: LocalFileDetectionPerformer {}

// This one wraps LocalFileDetectionPerformer inside DownloadableDetectionPerformer, and use them together.
class RemoteFileDetectionPerformer<LocalFileDetectionPerformerType: DownloadableDetectionPerformer>: DetectionPerformer {
    typealias ResultType = LocalFileDetectionPerformerType.ResultType
    private let localFileDetectionPerformer: LocalFileDetectionPerformerType
    
    init(remoteModelURL: URL) {
        let localModelURL = Self.localModelURL(for: remoteModelURL)
        localFileDetectionPerformer = LocalFileDetectionPerformerType(localModelURL: localModelURL)
    }
    
    static func localModelURL(for url: URL) -> URL {
        url.appendingPathExtension("local")
    }
}

class Detector<DetectionPerformerType: DetectionPerformer> {
    let performer: DetectionPerformerType

    init(performer: DetectionPerformerType) {
        self.performer = performer
    }
}

class SamplePerformer: DownloadableDetectionPerformer {
    required init(localModelURL: URL) {
        
    }
    
    typealias ResultType = Int
    typealias LocalModelType = String
}

這允許您執行以下操作:

let detectorB = Detector(performer: RemoteFileDetectionPerformer<SamplePerformer>(remoteModelURL: URL(string: "")!))

首先,我非常同意 Sweeper 的觀點。 這幾乎可以肯定是 ZAE832E9B5BDA26956DB45F3FAZAA8 中的 class inheritance 和 generics 使用不當。 通常,如果您認為您想要 class inheritance,您應該先使用其他工具。 首先,組合(你可以只傳遞函數或函數包)。 然后是協議。 查看您的類型,感覺您應該將“獲取數據的事物”與“檢測數據結果的事物”分開並組合兩者。

也就是說,這個問題是普遍的並且非常合理,並且有一個解決方案。 將類型作為參數傳遞:

init(performer: LocalFileDetectionPerformerType.Type, remoteModelURL: URL) { ... }

然后,當您調用它時,而不是顯式指定類型,而是傳遞類型:

let detectorB =
    Detector(performer: RemoteFileDetectionPerformer(performer: SamplePerformer.self,
                                                     remoteModelURL: URL(string: "https://example.com")!))

類型將自動計算出來:

Detector<Int, RemoteFileDetectionPerformer<Int, String, SamplePerformer>>

如果在某些情況下可以通過其他方式從上下文中知道類型,則可以添加默認參數:

init(performer: LocalFileDetectionPerformerType.Type = LocalFileDetectionPerformerType.self, ...

然后你可以在不需要的時候去掉這個參數。

暫無
暫無

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

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