我有一个协议:
protocol Adjustable: Equatable {
associatedtype T
var id: String { get set }
var value: T { get set }
init(id: String, value: T)
}
和一个符合它的结构:
struct Adjustment: Adjustable {
static func == (lhs: Adjustment, rhs: Adjustment) -> Bool {
return lhs.id == rhs.id
}
typealias T = CGFloat
var id: String
var value: T
}
我正在构建一个包装器类,其行为类似于Set
来处理这些属性的有序列表:
struct AdjustmentSet {
var adjustmentSet: [Adjustable] = []
func contains<T: Adjustable>(_ item: T) -> Bool {
return adjustmentSet.filter({ $0.id == item.id }).first != nil
}
}
let brightness = Adjustment(id: "Brightness", value: 0)
let set = AdjustmentSet()
print(set.contains(brightness))
但是那当然是行不通的,出现以下错误:
错误:协议“可调整”只能用作一般约束,因为它具有“自我”或相关类型要求 var AdjustmentSet:[可调] = []
环顾四周,我最初以为是因为该协议不符合Equatable
,但是后来我添加了它,但它仍然不起作用(或者我做错了)。
此外,我希望能够在此处使用通用名称,以便可以执行以下操作:
struct Adjustment<T>: Adjustable {
static func == (lhs: Adjustment, rhs: Adjustment) -> Bool {
return lhs.id == rhs.id
}
var id: String
var value: T
}
let brightness = Adjustment<CGFloat>(id: "Brightness", value: 0)
或者:
struct FloatAdjustment: Adjustable {
static func == (lhs: Adjustment, rhs: Adjustment) -> Bool {
return lhs.id == rhs.id
}
typealias T = CGFloat
var id: String
var value: T
}
let brightness = FloatAdjustment(id: "Brightness", value: 0)
并且仍然能够存储[Adjustable]
类型的数组,以便最终我可以做到:
var set = AdjustmentSet()
if set.contains(.brightness) {
// Do something!
}
或
var brightness = ...
brightness.value = 1.5
set.append(.brightness)
答案 0 :(得分:2)
您不能拥有类型为Adjustable
的项目数组,因为Adjustable
并不是真正的类型。这是一张蓝图,描述了一组类型,每个T
的可能值中都有一个。
要解决此问题,您需要使用类型橡皮擦https://medium.com/dunnhumby-data-science-engineering/swift-associated-type-design-patterns-6c56c5b0a73a
答案 1 :(得分:1)
利用亚历山大的建议取得了很大的进步;我能够使用一些嵌套的类类型来继承基本类型的擦除类,并使用符合AnyHashable
的通用协议,因此可以将其与集合一起使用!
// Generic conforming protocol to AnyHashable
protocol AnyAdjustmentProtocol {
func make() -> AnyHashable
}
protocol AdjustmentProtocol: AnyAdjustmentProtocol {
associatedtype A
func make() -> A
}
struct AdjustmentTypes {
internal class BaseType<T>: Hashable {
static func == (lhs: AdjustmentTypes.BaseType<T>, rhs: AdjustmentTypes.BaseType<T>) -> Bool {
return lhs.name == rhs.name
}
typealias A = T
var hashValue: Int { return name.hashValue }
let name: String
let defaultValue: T
let min: T
let max: T
var value: T
init(name: String, defaultValue: T, min: T, max: T) {
self.name = name
self.defaultValue = defaultValue
self.min = min
self.max = max
self.value = defaultValue
}
}
class FloatType: BaseType<CGFloat> { }
class IntType: BaseType<Int> { }
}
struct AnyAdjustmentType<A>: AdjustmentProtocol, Hashable {
static func == (lhs: AnyAdjustmentType<A>, rhs: AnyAdjustmentType<A>) -> Bool {
return lhs.hashValue == rhs.hashValue
}
private let _make: () -> AnyHashable
private let hashClosure:() -> Int
var hashValue: Int {
return hashClosure()
}
init<T: AdjustmentProtocol & Hashable>(_ adjustment: T) where T.A == A {
_make = adjustment.make
hashClosure = { return adjustment.hashValue }
}
func make() -> AnyHashable {
return _make()
}
}
struct Brightness: AdjustmentProtocol, Hashable {
func make() -> AnyHashable {
return AdjustmentTypes.FloatType(name: "Brightness", defaultValue: 0, min: 0, max: 1)
}
}
struct WhiteBalance: AdjustmentProtocol, Hashable {
func make() -> AnyHashable {
return AdjustmentTypes.IntType(name: "White Balance", defaultValue: 4000, min: 3000, max: 7000)
}
}
let brightness = Brightness().make()
let whiteBalance = WhiteBalance().make()
var orderedSet = Set<AnyHashable>()
orderedSet.insert(brightness)
print(type(of: orderedSet))
print(orderedSet.contains(brightness))
for obj in orderedSet {
if let o = obj as? AdjustmentTypes.FloatType {
print(o.value)
}
if let o = obj as? AdjustmentTypes.IntType {
print(o.value)
}
}
打印:
Set<AnyHashable>
true
0.0
特别感谢本文:https://medium.com/@chris_dus/type-erasure-in-swift-84480c807534,其中有一个简单而干净的示例,说明了如何实现泛型橡皮擦。