从使用的类型泛型之一推断出泛型类型

时间:2020-07-30 13:29:12

标签: ios swift generics generic-programming

我的应用中的Generic types结构非常复杂。这可行,但是有一个问题,在此链的末尾,我需要指定两次某些类型,因为它们需要用作某些类的泛型,而这些泛型类型之一也需要泛型。总是与之前的类型相同。像这样<A, B, C<A, B>>

这使它使用起来有点不愉快。是否有某种方法可以从A推断BC 这是带有剥离功能的示例代码:

// 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?

2 个答案:

答案 0 :(得分:2)

我觉得您在代码块的前半部分显示的类应该是协议。也就是说,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: "")!))

答案 1 :(得分:0)

首先,我完全同意Sweeper。几乎可以肯定,这在Swift中很少使用类继承和泛型。通常,如果您认为想要类继承,则应该首先接触其他工具。首先,组合(您可以只传递函数还是函数束)。然后是协议。查看您的类型,感觉您应该将“获取数据的事物”与“检测数据结果的事物”分开,然后将两者组成。

也就是说,问题是普遍且非常合理的,并且有解决方案。将类型作为参数传递:

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, ...

然后,您可以在不需要该参数时忽略该参数。

相关问题