Mar*_*sel 5 protocols existential-type swift
所以我正在实施以下内容:
LanguageType协议,符合HashableTranslateable获取(和设置)a[String]LanguageType// MARK: - LanguageType
protocol LanguageType: Hashable {
var description: String { get }
}
extension LanguageType {
var description: String { return "\(Self.self)" }
var hashValue: Int { return "\(Self.self)".hashValue }
}
func ==<T: LanguageType, U: LanguageType>(left: T, right: U) -> Bool {
return left.description == right.description
}
// MARK: - Translateable
protocol Translateable {
var translations: [LanguageType: [String]] { get set }
}
Run Code Online (Sandbox Code Playgroud)
LanguageType与往常一样,Swift 在协议的使用方式上存在问题:
据我所知,这与 Swift 不支持Existentials有关,这导致协议实际上并不是第一类类型。
在泛型的上下文中,这个问题通常可以通过类型擦除的包装器来解决。
就我而言,没有泛型或关联类型。
我想要实现的是必须translations.Key是任何 LanguageType符合的通用类型,而不仅仅是一种通用类型LanguageType。
例如,这是行不通的:
protocol Translateable {
typealias Language: LanguageType
var translations: [Language: [String]] { get set }
}
Run Code Online (Sandbox Code Playgroud)
出于某种原因,我就是想不出一种方法来实现这一目标。我发现听起来我需要某种类型擦除的包装,正如我想要的那样
translations.Key成为任何LanguageType
我想我需要删除确切的类型,它应该LanguageType符合Translateable. 我可以做什么来解决这个问题?
更新1:
正如刚刚在这个问题中确定的那样,LanguageType实际上具有关联的类型要求(遵守它的一致性Equatable)。因此,我将尝试创建一个围绕LanguageType.
更新 2:
所以我意识到,创建类型擦除的包装器实际上LanguageType并不能解决问题。我创建了AnyLanguage:
struct AnyLanguage<T>: LanguageType {
private let _description: String
var description: String { return _description }
init<U: LanguageType>(_ language: U) { _description = language.description }
}
func ==<T, U>(left: AnyLanguage<T>, right: AnyLanguage<U>) -> Bool {
return left.description == right.description
}
Run Code Online (Sandbox Code Playgroud)
如果我现在用它来代替LanguageType它不会有多大作用,因为Translateable仍然需要关联的类型:
protocol Translateable {
typealias T
var translations: [AnyLanguage<T>: [String]] { get set }
}
Run Code Online (Sandbox Code Playgroud)
我从以下位置删除了泛型AnyLanguage:
struct AnyLanguage: LanguageType {
private(set) var description: String
init<T: LanguageType>(_ language: T) { description = language.description }
}
func ==(left: AnyLanguage, right: AnyLanguage) -> Bool {
return left.description == right.description
}
protocol Translateable {
var translations: [AnyLanguage: [String]] { get set }
}
Run Code Online (Sandbox Code Playgroud)
不知道为什么我T在 Update 2 中引入,因为它没有做任何事情。但这现在似乎有效。
解决方案似乎是一个类型擦除的包装器。类型擦除通过创建包装类型来修复无法将具有关联类型 (PAT) 的协议作为一等公民使用的问题,该包装类型仅公开其包装的协议定义的属性。
\n\n在这种情况下,LanguageType是PAT,因为它采用了Equatable(它符合,因为它采用了Hashable):
protocol LanguageType: Hashable { /*...*/ }\nRun Code Online (Sandbox Code Playgroud)\n\n因此它不能用作协议中的第一类类型Translatable:
protocol Translatable {\n var translations: [LanguageType: [String]] { get set } // error\n}\nRun Code Online (Sandbox Code Playgroud)\n\n为 定义关联类型并Translatable不能解决问题,因为这会将 限制LanguageType为一种特定类型:
protocol Translatable {\n typealias Language: LanguageType\n\n var translations: [Language: [String]] { get set } // works\n} \n\nstruct MyTranslatable<T: LanguageType>: Translatable {\n var translations: [T: [String]] // `T` can only be one specific type\n\n //...\n}\nRun Code Online (Sandbox Code Playgroud)\n\n如前所述,解决方案是类型擦除包装器AnyLanguage(Apple 对类型擦除包装器使用相同的命名约定。例如AnySequence):
// `AnyLanguage` exposes all of the properties defined by `LanguageType`\n// in this case, there\'s only the `description` property\nstruct AnyLanguage: LanguageType {\n private(set) var description: String\n\n // `AnyLanguage` can be initialized with any type conforming to `LanguageType`\n init<T: LanguageType>(_ language: T) { description = language.description }\n}\n\n// needed for `AnyLanguage` to conform to `LanguageType`, as the protocol inherits for `Hashable`, which inherits from `Equatable`\nfunc ==(left: AnyLanguage, right: AnyLanguage) -> Bool {\n return left.description == right.description\n}\n\n// the use of `AnyLanguage` allows any `LanguageType` to be used as the dictionary\'s `Key`, as long as it is wrapped as `AnyLanguage`\nprotocol Translateable {\n var translations: [AnyLanguage: [String]] { get set }\n}\nRun Code Online (Sandbox Code Playgroud)\n\n此实现现在允许以下操作:
\n\nstruct SomethingTranslatable: Translatable\xc2\xa0{\n var translations: [AnyLanguage: [String]] = [:]\n}\n\nfunc ==(left: SomethingTranslatable, right: SomethingTranslatable) -> Bool { /*return some `Bool`*/ }\n\nstruct English: LanguageType { }\nstruct German: LanguageType { }\n\nvar something = SomethingTranslatable()\nsomething.translations[AnyLanguage(English())] = ["Hello", "World"]\nlet germanWords = something.translations[AnyLanguage(German())]\nRun Code Online (Sandbox Code Playgroud)\n\n符合 的不同类型LanguageType现在可以用作Key. 唯一的语法差异是必要的初始化AnyLanguage:
AnyLanguage(English())\nRun Code Online (Sandbox Code Playgroud)\n
| 归档时间: |
|
| 查看次数: |
497 次 |
| 最近记录: |