从使用的泛型类型之一推断泛型类型
Infering Generic types from one of used types generics
我的应用程序中有一个非常复杂的结构 Generic types
。这行得通,但有一个问题,在这个链的末尾,我需要指定某些类型 2 次,因为它们需要用作某些 class 的泛型,而这些泛型类型之一,也需要通用类型。它们总是与之前的类型相同。它是这样的 <A, B, C<A, B>>
这让使用起来有点不愉快。有没有办法让它从 C
推断出 A
和 B
这是示例代码,具有剥离的功能:
// 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?
我觉得您在代码块前半部分显示的 类 应该是协议。也就是说,DetectionPerformer
、LocalFileDetectionPerformer
、DownloadableDetectionPerformer
都应该是协议。他们似乎没有任何真正的实施,正如您的评论“现在我可以实施一些特定的表演者,这将做真正的工作”中显而易见的那样。如果您有任何想要放入其中的实现,大多数时候都可以将其放入扩展中。为什么使它们成为协议可以解决问题?因为这样我们就可以使用关联类型而不是类型参数。
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 的观点。这几乎可以肯定是 Swift 中对 class 继承和泛型的错误使用。通常,如果您认为需要 class 继承,则应该先使用其他工具。首先,组合(你可以只传递函数或函数包)。然后是协议。查看您的类型,感觉您应该将“获取数据的东西”与“检测数据结果的东西”分开并将两者组合在一起。
也就是说,问题很普遍,很合理,有解决办法。将类型作为参数传递:
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, ...
那么你可以在不需要的时候去掉这个参数。
我的应用程序中有一个非常复杂的结构 Generic types
。这行得通,但有一个问题,在这个链的末尾,我需要指定某些类型 2 次,因为它们需要用作某些 class 的泛型,而这些泛型类型之一,也需要通用类型。它们总是与之前的类型相同。它是这样的 <A, B, C<A, B>>
这让使用起来有点不愉快。有没有办法让它从 C
推断出 A
和 B
这是示例代码,具有剥离的功能:
// 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?
我觉得您在代码块前半部分显示的 类 应该是协议。也就是说,DetectionPerformer
、LocalFileDetectionPerformer
、DownloadableDetectionPerformer
都应该是协议。他们似乎没有任何真正的实施,正如您的评论“现在我可以实施一些特定的表演者,这将做真正的工作”中显而易见的那样。如果您有任何想要放入其中的实现,大多数时候都可以将其放入扩展中。为什么使它们成为协议可以解决问题?因为这样我们就可以使用关联类型而不是类型参数。
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 的观点。这几乎可以肯定是 Swift 中对 class 继承和泛型的错误使用。通常,如果您认为需要 class 继承,则应该先使用其他工具。首先,组合(你可以只传递函数或函数包)。然后是协议。查看您的类型,感觉您应该将“获取数据的东西”与“检测数据结果的东西”分开并将两者组合在一起。
也就是说,问题很普遍,很合理,有解决办法。将类型作为参数传递:
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, ...
那么你可以在不需要的时候去掉这个参数。