其实……为了回答我自己的问题……我写了一些东西……
这是协议 OptionSetAssociated 及其扩展:
public protocol OptionSetAssociated: OptionSet where RawValue: BinaryInteger {
var store: [RawValue: Any] { get set }
}
extension OptionSetAssociated {
public init<T>(rawValue: RawValue, value: T) {
self.init(rawValue: rawValue)
self.store[rawValue] = value
}
fileprivate init(rawValue: RawValue, store: [RawValue: Any]) {
self.init(rawValue: rawValue)
self.store = store
}
fileprivate static func combinedStore(_ old: [RawValue: Any], new: [RawValue: Any]) -> [RawValue: Any] {
new.map {$0.key}.reduce(into: old) {
$0[$1] = new[$1] ?? old[$1]
}
}
fileprivate static func storeOverride(_ store: [RawValue: Any], member: RawValue?, value: Any?) -> [RawValue: Any] {
guard let member: RawValue = member else { return store }
var store: [RawValue: Any] = store
store[member] = value
return store
}
public func getValue<T>(key: RawValue) -> T? {
self.store[key] as? T
}
mutating public func formUnion(_ other: __owned Self) {
self = Self(rawValue: self.rawValue | other.rawValue, store: Self.combinedStore(self.store, new: other.store))
}
}
extension OptionSet where Self: OptionSetAssociated, Self == Element {
@discardableResult
public mutating func insert(
_ newMember: Element
) -> (inserted: Bool, memberAfterInsert: Element) {
let oldMember = self.intersection(newMember)
let shouldInsert = oldMember != newMember
var result = (
inserted: shouldInsert,
memberAfterInsert: shouldInsert ? newMember : oldMember)
if shouldInsert {
self.formUnion(newMember)
} else {
self.store = Self.storeOverride(
Self.combinedStore(self.store, new: newMember.store),
member: newMember.rawValue, value: newMember.store[newMember.rawValue])
result.memberAfterInsert.store[newMember.rawValue] = newMember.store[newMember.rawValue]
}
return result
}
@discardableResult
public mutating func remove(_ member: Element) -> Element? {
var intersectionElements = intersection(member)
guard !intersectionElements.isEmpty else {
return nil
}
let store: [RawValue: Any] = self.store
self.subtract(member)
self.store = store
self.store[member.rawValue] = nil
intersectionElements.store = Self.storeOverride([:], member: member.rawValue, value: store[member.rawValue])
return intersectionElements
}
@discardableResult
public mutating func update(with newMember: Element) -> Element? {
let previousValue: Any? = self.store[newMember.rawValue]
var r = self.intersection(newMember)
self.formUnion(newMember)
self.store[newMember.rawValue] = newMember.store[newMember.rawValue]
if r.isEmpty { return nil } else {
r.store = Self.storeOverride([:], member: newMember.rawValue, value: previousValue)
r.store[newMember.rawValue] = previousValue
return r
}
}
}
这是一个结构,它使用它并像普通的 OptionSet 一样工作..
public struct TestSet: OptionSetAssociated {
public typealias RawValue = Int
public let rawValue: Int
public var store: [RawValue : Any] = [:]
public init(rawValue: RawValue) {
self.rawValue = rawValue
}
}
extension TestSet { // Members
public static var bool: TestSet { TestSet(rawValue: 1 << 0) }
public static var int: TestSet { TestSet(rawValue: 1 << 1) }
public static var string: TestSet { TestSet(rawValue: 1 << 2) }
public static var optString: TestSet { TestSet(rawValue: 1 << 3) }
public static func int(_ value: Int) -> TestSet {
TestSet(rawValue: TestSet.int.rawValue, value: value)
}
public static func string(_ value: String) -> TestSet {
TestSet(rawValue: TestSet.string.rawValue, value: value)
}
public static func optString(_ value: String?) -> TestSet {
TestSet(rawValue: TestSet.optString.rawValue, value: value)
}
}
extension TestSet { // Member options
public var int: Int? {
self.getValue(key: TestSet.int.rawValue) ?? ( self.contains(TestSet.int) ? Int() : nil ) // Returns default Int() if member is included and no value was given...
}
public var string: String? {
self.getValue(key: TestSet.string.rawValue) ?? ( self.contains(TestSet.string) ? String() : nil )
}
public var optString: String? { // Returns nil when value was not given
self.getValue(key: TestSet.optString.rawValue)
}
}
这里是一些调试的扩展(我选择了以前对迭代器的依赖):
extension TestSet: CustomStringConvertible {
public var description: String {
var members: [String] = []
var vars: [String] = []
if self.contains(TestSet.bool) { members.append("bool" ) }
if self.contains(TestSet.int) { members.append("int") }
if self.contains(TestSet.string) { members.append("string") }
if self.contains(TestSet.optString) { members.append("Optional<String>") }
if let int: Int = self.int { vars.append("Int(" + int.description + ")") }
if let string: String = self.string { vars.append("String(" + string + ")") }
if let optString: String = self.optString { vars.append("Optional<String>(" + optString + ")")}
return "Members: " + (members.isEmpty ? ["none"] : members).joined(separator: ", ") + "\nVariables: " + vars.joined(separator: ", ")
}
}
试试看。初始化就像普通的 OptionSet..
var testset: TestSet = []
testset = [.int, .string("hello")]
testset.remove(.int)
testset.update(with: .string("world"))
testset.insert(.int(10))
print(testset.description)
结果:
成员:整数、字符串
变量:Int(10), String(world)
因此,在这个版本中,每个成员都可以使用一个值(除非它是一个元组..)——当成员离开集合时,它会被删除。值保存在变量存储中,这是一个以 RawValue 为关键元素的字典。因为我使用字典而不是 KeyValuePairs - 要求 RawValue 符合 Hashable(BinaryInteger)。
在示例中,如果没有为不使用可选值的成员提供值,则返回默认值,然后有一个成员具有可选值。也是一个毫无价值的传统成员。
创建与普通的 OptionSet 相同,不同之处在于:
public var store: [RawValue : Any] = [:]
不幸的是,我确信我忘记考虑一些事情,这使得我的实现仍然不完美,尽管它并不重要或太重要.. 我刚刚在我的脑海里有它,但失去了它在那之后,它来得很快:)