【问题标题】:Swift where condition to check if a property is implementedSwift where条件检查属性是否实现
【发布时间】:2017-05-20 16:44:33
【问题描述】:

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

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

这篇文章的要点是,我需要一种简洁的方法来为可选的 String 提供 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 泛型,我创建了一个操作符以更快地访问和更方便地使用(至少我是这么认为的。请随时纠正我,我总是很乐意学习新事物并提高自己)。

我称之为“非空 nil 合并”运算符(谁能想出更好的名字?我觉得我在命名事物时很烂 :/ )。希望有一天它可以帮助某人:

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)

【问题讨论】:

  • 你的orWhenNilOrEmpty 函数真的需要通用吗?你不能用Wrapped替换T吗?或者您的 API 是否旨在支持任意类型的默认值(但在最后一行,您的强制转换可能总是成功)?
  • 你是对的。 T 实际上根本不需要。感谢您的提示!

标签: swift generics protocols extension-methods


【解决方案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 和空),它们似乎代表完全相同的事物。

【讨论】:

  • 镜像不能用作扩展的约束。并且为每个可选值提供方法而不考虑包装类型将非常糟糕。
  • 当然,你是对的!正在考虑另一种设计......无论如何,谢谢;)
  • “let x = y, !x.isEmpty ...”正是我想要摆脱的。 :) 无论如何,我已经认为无法检查功能或属性是否存在,因此感谢您的确认。顺便说一句,我现在更进一步,使我的函数成为一个名为 ??? 的运算符。也许有点过头了,但对我来说,这比在实际的客户项目中使用它更像是一种学习。好吧,在我自己的个人项目中,我可能会使用它:D
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-11-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-28
  • 1970-01-01
相关资源
最近更新 更多