您的当前位置:首页你在代码中使用if case了吗?

你在代码中使用if case了吗?

2024-12-13 来源:哗拓教育

模式匹配是swift语言的亮点,如果运用得当,能有效提升代码的 逼格。比方说if case,guard case,for case。如过你还没有使用过它们,或者不确信它们是否可行,不妨试试下面的内容。

if case

case let x = y 表达式可以完成检查 y 是否匹配模式 x。

表达式 if case let x = y { … } 则等同于表达式 switch y { case let x: … } 。前者是后者的一种简写,switch适用于多分支的模式匹配,而if case则适合于单条case的匹配情况。
比方说我们有这样一个枚举:

enum Media {
  case Book(title: String, author: String, year: Int)
  case Movie(title: String, director: String, year: Int)
  case WebSite(urlString: String)
}

如果使用switch进行匹配的话:

let m = Media.Movie(title: "Captain America: Civil War", director: "Russo Brothers", year: 2016)

switch m {
  case let Media.Movie(title, _, _):
    print("This is a movie named \(title)")
  default: () 
}

我们只用到了单分支匹配,但需要描述 switch,default,啰嗦不?所以要:变

let m = Media.Movie(title: "Captain America: Civil War", director: "Russo Brothers", year: 2016)

if case let Media.Movie(title, _, _) = m {
  print("This is a movie named \(title)")
}

这样写有没有感觉就像理了头发,清爽了许多,心情瞬间美好起来?其实不只是模式匹配,完全可以在混合一下条件表达式:

  if case let Media.Movie(_, _, year) = m where year < 1888 {
    print("Something seems wrong: the movie's year is before the first movie ever made.")
  }

通过if case表达式,我们几乎把switch的所有本事都搬来了,愉快。

guard case

从模式匹配的角度来看,guard case与if case的语法结构是完全相同的,它们的区别仅仅体现在guard关键字与if关键字的区别上。直接看一个例子。

enum NetworkResponse {
  case Response(NSURLResponse, NSData)
  case Error(NSError)
}

func processRequestResponse(response: NetworkResponse) {
  guard case let .Response(urlResp, data) = response,
    let httpResp = urlResp as? NSHTTPURLResponse
    where 200..<300 ~= httpResp.statusCode else {
      print("Invalid response, can't process")
      return
  }
  print("Processing \(data.length) bytes…")
}

for case

把for、case这两个关键字联合起来,能够让我们在迭代集合的时候,进行模式匹配,这感觉就好像我们在for迭代中,使用了if case。我们先来创建一个Media类型的数组,数组中包括电影和图书。

let mediaList: [Media] = [
  .Book(title: "Harry Potter and the Philosopher's Stone", author: "J.K. Rowling", year: 1997),
  .Movie(title: "Harry Potter and the Philosopher's Stone", director: "Chris Columbus", year: 2001),
  .Book(title: "Harry Potter and the Chamber of Secrets", author: "J.K. Rowling", year: 1999),
  .Movie(title: "Harry Potter and the Chamber of Secrets", director: "Chris Columbus", year: 2002),
  .Book(title: "Harry Potter and the Prisoner of Azkaban", author: "J.K. Rowling", year: 1999),
  .Movie(title: "Harry Potter and the Prisoner of Azkaban", director: "Alfonso Cuarón", year: 2004),
  .Movie(title: "J.K. Rowling: A Year in the Life", director: "James Runcie", year: 2007),
  .WebSite(urlString: "https://en.wikipedia.org/wiki/List_of_Harry_Potter-related_topics")
]

现在我们来完成这样一个任务,我们把集合中所有的电影信息打印出来:

for case let Media.Movie(title, _, year) in mediaList {
  print(" - \(title) (\(year))")
}

如果配合上where子句,我们会得到更加强大的能力,比方说我们只打印某一位导演的电影。

for case let Media.Movie(title, director, year) in mediaList where director == "Chris Columbus" {
  print(" - \(title) (\(year))")
}

百尺竿头再进一步

我们现在扩展一下Media,让它可以描述title和种类,我们用extension关键字。

extension Media {
  var title: String? {
    switch self {
    case let .Book(title, _, _): return title
    case let .Movie(title, _, _): return title
    default: return nil
    }
  }
  var kind: String {
    switch self {
    case .Book: return "Book"
    case .Movie: return "Movie"
    case .WebSite: return "Web Site"
    }
  }
}

现在我们打印一下mediaList中所有元素的title和kind

for case let (title?, kind) in mediaList.map({ ($0.title, $0.kind) })
  where title.hasPrefix("Harry Potter") {
    print(" - [\(kind)] \(title)")
}

这个代码的可读性并不是很高,我们先来简单解释一下上面这个代码片段

  • 首先是map函数将 mediaList 映射成一个[(String?,String)]类型的数组。
  • 然后我们使用case let 进行了title 与 kind的值绑定,因为我们指明了title?所以要求数组元素中必须包含title
  • 最后使用where子句进行条件表达式过滤。

因为代码的可读性决定了代码了可维护性,所以写出容易阅读的代码是至关重要的,我们其实可以使用guard关键字,让上面的这个段代码变得更加清晰简洁(虽然长度会略长一些)。

for media in mediaList {
  guard let title = media.title else {
    continue
  }
  guard title.hasPrefix("Harry Potter") else {
    continue
  }
  print(" - [\(media.kind)] \(title)")
}

希望if case可以更多的出现在我们的代码中,来让我们的代码更优美。

本文参考

显示全文