扩展 Set 在 swift 中的插入以用于自定义逻辑



我需要在一个Set中有自定义逻辑,用于定义何时可以插入Hashable。

首先,我试图用一个观测者来解决这个问题

var Tenants: Set<Tenant> = [] {
willSet {
// to the business logic here
// ...

但作为一个观察者,我不能返回错误。所以我尝试扩展Set来覆盖insert方法。

extension Set where Element == Tenant {
@inlinable mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element){
// .... do my logic here ...
return (true, newMember)
}
}

到目前为止,这是有效的,该方法将被调用。我可以返回true,如果我的逻辑甚至没有通过false。好的,但是我如何将Element添加到集合中?super.insert()。返回正确,但Set为空。如何将元素添加到具体集合中?

迄今为止的实施

/// Global set of known tenants
var Tenants: Set<Tenant> = [] {
willSet {
let newTenants = newValue.symmetricDifference(Tenants)
guard let newTenant = newTenants.first else {
Logging.main.error("Can not find tenant to add.")
return
}
Logging.main.info("Will add new Tenant (newTenant.name) [(newTenant.ident)]")
}
}
extension Set where Element == Tenant {
@inlinable mutating func insert(_ newMember: Element) -> (inserted: Bool, memberAfterInsert: Element){
print("Check to add...")
// .... do my logic here ...
// ok
return (true, newMember)
}
}

结果是:

Check to add...
error : Can not find tenant to add.
Check to add...
error : Can not find tenant to add.

这似乎适用于"在这里做我的逻辑";

self = self.union([newMember])

编辑:因为这打破了Set的语义,我认为最好写成这样:

struct CheckedSet<T: Hashable> {
private(set) var wrappedSet: Set<T> = []
var shouldInsert: (T) -> Bool = { _ in true }
mutating func maybeInsert(_ t: T) {
guard shouldInsert(t) else { return }
wrappedSet.insert(t)
}
}
var cs = CheckedSet<String>()
cs.shouldInsert = { str in str.allSatisfy(.isLowercase) }
cs.maybeInsert("HELLO")
cs.wrappedSet  // []
cs.maybeInsert("hello")
cs.wrappedSet // ["hello"]

我会使用属性包装器:

@propertyWrapper
struct TenantsSet {
var wrappedSet: Set<Tenant>

struct Projected {
let error: Bool
}

var projectedValue = Projected(error: false)

var wrappedValue: Set<Tenant> {
get { wrappedSet }
set {
print("some custom logic")
// set projectedValue appropriately
wrappedSet = newValue
}
}

init(wrappedValue: Set<Tenant>) {
wrappedSet = wrappedValue
}
}

这允许通过检查投影值上的error属性来报告错误:

@TenantsSet var tenants = []
func f() {
tenants = [Tenant()]
if $tenants.error {

}
}

正如Swift指南所说:

扩展为现有类、结构、枚举或协议类型添加了新功能。

您不应该使用它们来修改现有行为。这会让代码的读者感到非常困惑。如果要使用扩展来执行此操作,则应声明一个具有不同签名的新方法。也许可以称之为insert(newTenant:)

最新更新