Swift where 条件检查 属性 是否实现

Swift where condition to check if a property is implemented

我刚刚在 Swift 中找到了另一种充分利用协议和协议扩展的方法,方法是扩展可选协议以添加一个函数,这样我就可以提供默认值。

我在这里写了一篇关于此的博客 post:https://janthielemann.de/random-stuff/providing-default-values-optional-string-empty-optional-string-swift-3-1/

post 的要点是,我需要一种简洁明了的方法来为可选字符串提供默认值,即 nil 或空。为此,我创建了一个 Emptyable 协议并扩展了 Optional 协议,如下所示:

protocol Emptyable {
    var isEmpty: Bool { get }
}

extension Optional where Wrapped: Emptyable {
    func orWhenNilOrEmpty<T: Emptyable>(_ defaultValue: T) -> T {
        switch(self) {
        case .none:
            return defaultValue
        case .some(let value) where value.isEmpty:
            return defaultValue
        case .some(let value):
            return value as! T
        }
    }
}

extension String: Emptyable {}

现在的问题是:有没有办法摆脱 Emptyable 协议,而是有条件地检查 属性 或函数是否是由通用类型实现,以便我自动为每个具有 isEmpty?

的类型获取 orWhenNilOrEmpty()

更新

正如 Paulo 所建议的那样,T 泛型实际上是不需要的,我创建了一个运算符,以便更快地访问和更方便地使用(至少我是这么认为的。随时纠正我,我总是乐于学习新事物并提高自己)。

我称它为 "not empty nil coalescing" 运算符(谁能想出更好的名字?我觉得我很不擅长命名 :/ )。希望有一天它能帮助某人:

protocol Emptyable {
    var isEmpty: Bool { get }
}

infix operator ???: NilCoalescingPrecedence

extension Optional where Wrapped: Emptyable {
    func orWhenNilOrEmpty(_ defaultValue: Wrapped) -> Wrapped {
        switch(self) {
        case .none:
            return defaultValue
        case .some(let value) where value.isEmpty:
            return defaultValue
        case .some(let value):
            return value
        }
    }

    static func ???(left: Wrapped?, right: Wrapped) -> Wrapped {
        return left.orWhenNilOrEmpty(right)
    }
}

extension String: Emptyable {}

extension Array: Emptyable {}

extension MyStruct: Emptyable {
    let text: String
    let number: Int
    var isEmpty: Bool { return text.isEmpty && number == 0 }
    init(text: String, number: Int) {
        self.text = text
        self.number = number
    }
}

let mandatoryNotEmptyString = optionalOrEmptyString ??? "Default Value"
let mandatoryNotEmptyStruct = optionalOrEmptyStruct ??? MyStruct(name: "Hello World", number: 1)

不,您不能在不使用协议的情况下查询对象或值是否具有特定的 属性 作为扩展的约束。这需要以一种目前未在 Swift 中实现的方式进行反思。此外,isEmpty 属性 对于不同的类型可能具有不同的含义,因此测试是否存在方法或 属性 而不是协议可能会导致意外行为。

你可以直接写

if let unwrappedString = optionalString, !unwrappedString.isEmpty {
    // Do stuff
} else {
    // Use default value
}

无需协议或扩展,可读性强。

在 Swift 4 中,它将在今年秋天推出,String will conform to BidirectionalCollection,它继承自 CollectionCollection 协议提供了一个 isEmpty 属性,所以你的扩展可以是

extension Optional where Wrapped: Collection {
     // ...
}

但即便如此,您首先应该考虑在存储空字符串时将它们设置为 nil,因为您现在有两个状态(nil 和 empty),它们似乎代表完全相同的事物。