如何添加可选的字符串扩展名?


73

您可以这样创建一个字符串扩展名:

extension String {
   func someFunc -> Bool { ... }
}

但是,如果您希望它应用于可选字符串怎么办?

var optionalString :String? = ""
optionalString!.someFunc() /* String? does not have a member someFunc */

您需要始终将其
拆封

所以我解开了它,但是也许我做错了吗?optionalString!.someFunc()
vol7ron 2015年

如果您确定可选选项不会返回nil,那就可以,否则请使用
Leo Dabus 2015年

有时为零,这就是为什么使用可选的原因。我仍在调试中,我怀疑我的Playground刚刚不同步。我可能在一秒钟内结束这个问题。
vol7ron 2015年

@LeonardoSavioDabus是的,这就是我希望放入扩展名并使用扩展名解决的问题。我不喜欢嵌套的IF语句。我的猜测是,Swift的未来版本最终将在此
基础

Answers:


184

在Swift 3.1中,您还可以向可选值添加扩展:

extension Optional where Wrapped == String {
  var isBlank: Bool {
    return self?.isBlank ?? true
  }
}

它的用法是什么样的?
shim

1
if myOptionalString?.isBlank {给可选类型布尔值?还没有展开。
shim

并得到一个Type Wrapped constrained to non-protocol type String错误。 这是有关Reddit的一个可能相关的讨论,看来将来可能会解决此问题。
shim

有可能这样做where Wrapped == T吗?
Declan McKenna

3
用法是myOptionalString.isBlank。没有,?因为您是在可选本身而不是在上调用它String
lammert

12

您可以这样做:

protocol OptionalType { typealias A; var opt: A? { get } }
extension Optional: OptionalType { var opt: A? { return self } }

protocol StringType { var get: String { get } }
extension String: StringType { var get: String { return self } }

extension Optional where Wrapped: StringType {
  func getOrElse(s: String) -> String {
    return self.opt?.get ?? s
  }
}

和:

let optStr: String? = nil
optStr.getOrElse("hello world")

之所以无法约束,OptionalString因为那是因为struct。通过为每个协议创建伪协议,现在我们可以根据需要进行约束。

我觉得迅速放弃了很多东西,只是为了让初学者更容易学习,或者这门语言还不够成熟。


这是新的吗?我以为我过去曾尝试扩展可选类型
vol7ron

@ vol7ron这是现在可以通过与SWIFT 2.来到协议扩展
丹尼尔信

8

扩展名Optional返回一个String

从Swift 3开始,您不能直接将扩展方法限制为optional String。正如Daniel Shin的答案所解释的,您可以通过协议获得等效的结果。

但是,您可以在任何类型的Optional上创建扩展方法,并且我发现了一些具有String返回值的有用方法。这些扩展有助于将值记录到控制台。String当我想用空字符串替换可能的nil时,已在可选对象上使用了asStringOrEmpty()。

extension Optional {
    func asStringOrEmpty() -> String {
        switch self {
            case .some(let value):
                return String(describing: value)
            case _:
                return ""
        }
    }

    func asStringOrNilText() -> String {
        switch self {
            case .some(let value):
                return String(describing: value)
            case _:
                return "(nil)"
        }
    }
}

使用示例:

var booleanValue: Bool?
var stringValue: String?
var intValue: Int?

print("booleanValue: \(booleanValue.asStringOrNilText())")
print("stringValue: \(stringValue.asStringOrNilText())")
print("intValue: \(intValue.asStringOrNilText())")

booleanValue = true
stringValue = "text!"
intValue = 41

print("booleanValue: \(booleanValue.asStringOrNilText())")
print("stringValue: \(stringValue.asStringOrNilText())")
print("intValue: \(intValue.asStringOrNilText())")

控制台输出:

booleanValue: (nil)
stringValue: (nil)
intValue: (nil)

booleanValue: true
stringValue: text!
intValue: 41

 

Optional 与nil指针不同

这些扩展说明anOptional与nil指针不同。anOptionalenum指定类型(Wrapped)的a,表示它包含或不包含值。您可以在Optional“容器”即使其中可能不包含值。

摘自Swift可选声明

enum Optional<Wrapped> : ExpressibleByNilLiteral {

    /// The absence of a value.
    case none

    /// The presence of a value, stored as `Wrapped`.
    case some(Wrapped)

    ...
}

在代码中,通常使用nil文字而不是显式.none枚举的情况来编写不存在值的情况。


5

在Swift 4.1中,我遇到了Optional is ambiguous for type lookup in this context构建错误。要解决此问题,必须将Swift命名空间显式添加到类型中:

extension Swift.Optional where Wrapped == String {
    var isBlank: Bool {
        return self?.isBlank ?? true
    }
}

4
extension Optional where Wrapped == String {
var isNil: Bool {
    return self == nil
}

上面的答案(由@Vlad Hatko编写)效果很好,但是在Swift 4中存在一些问题,因此我将其更改为此。


3

从Xcode 9.3开始,您可以使用@Vladyslav的答案的以下轻微修改:

extension Optional where Wrapped == String {

    var isEmpty: Bool {
        return self?.isEmpty ?? true
    }

}

2

更新:有关适用于Swift 2及更高版本的解决方法,请参见Daniel Shin的答案


可选字符串本身不是类型,因此您不能在可选类型上创建扩展名。在Swift中,anOptional只是一个枚举(加上一些语法糖),可以为None,也Some可以包装一个值。要使用String方法,您需要打开optionalString。您可以轻松地使用可选链接来实现此目的:

optionalString?.someFunc()

如果optionalString不是nilsomeFunc将对其进行调用。另一种(不太简洁)的方法是optionalString在尝试调用该方法之前,使用可选绑定来确定是否具有值:

if let string = optionalString {
    string.someFunc()    // `string` is now of type `String` (not `String?`)
}

在下面的注释示例中,您无需嵌套多个if语句,可以检查可选字符串是否在单个字符串中为空字符串if

if optionalString?.isEmpty == true {
    doSomething()
}

这工作,因为表达式optionalString?.isEmpty返回一个可选的布尔(即truefalsenil)。所以doSomething()才会被调用,如果optionalString没有 nil如果该字符串是空的。

另一种选择是:

if let string = optionalString where string.isEmpty {
    doSomethingWithEmptyString(string)
}

我试图创建一个扩展以避免可选绑定。看来这将是不可能的
vol7ron 2015年

@ vol7ron我可以问为什么吗?可选的链接/绑定是否不便?
Stuart

考虑一个要检查的可选字符串,其中是否包含字符。据我所知,您必须使用嵌套的if,if let str=optString { if str.isEmpty { doSomething(); } }我希望这是isBlank可选字符串的扩展
vol7ron 2015年

我的示例(用注释)还不完整,但是isBlank通常为nilisEmpty结果为true。
vol7ron 2015年

是的,您提出的方法可读性不如其他语言。 if optStr.isBlank { … }繁荣。对于像Swift这样的新事物,您会认为最终目标是可读性,因为可读性是可维护的。
vol7ron 2015年

1

发现一些技巧迅速3

class A{
    var name:String!;
    init(_ name:String?){
        self.name = name;
    }
}

extension Optional where Wrapped == String {
    func compareText(_ other:String?)->Bool{
        switch (self,other){
        case let(a?,b?):
            return a < b;
        case (nil,_):
            return true;
        default:
            return false;
        }
    }
}

let words:[A] = [A("a"),A(nil),A("b"),A("c"),A(nil)];

// let sorted = words.sorted{ 0.name.compareText($1.name) }
// trick
let sorted = words.sorted{ ($0.name as String?).compareText($1.name) }

print(sorted.map{$0.name});

1

您可以创建一个可选的字符串扩展名。我执行以下操作将可选字符串设置为null,如果它为nil:

extension Optional where Wrapped == String {

    mutating func setToEmptyIfNil() {
        guard self != nil else {
            self = ""
            return
        }
    }

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.