Swift-style-guide: 可变阴影会导致代码难以阅读。

创建于 2014-12-15  ·  22评论  ·  资料来源: raywenderlich/swift-style-guide

我认为我们应该重新考虑在展开时使用影子变量的立场。 我们当前的论点是,如果您尝试使用错误的变量,编译器会纠正您。 我认为我们忽略了这个论点中的一个根本缺陷:它使代码在没有实际编译时更难阅读。

我看到在编译器之外读取这样的代码会导致问题的三个主要地方:

  1. 斯威夫特初学者。 两个具有相同名称的事物具有如此根本不同的行为是非常令人困惑的。 对于我们这些来自 Objective-C 的人来说尤其如此,试图用相同的名称定义两个东西会导致编译器警告。
  2. 代码审查。 对于在合并之前必须查看代码的任何人,都需要进行额外的审查以确保“等等,这个人是在尝试分配可选值还是未包装值?”
  3. 图书编辑。 我们是否期望我们所有的图书编辑不仅采用示例代码,而且采用任何在书中键入的代码并确保它可以编译?

举个例子:

func createNewPerson(name: String?) -> Person {
  let person = Person()
  if let name = name {
    person.name = name
  }
 return person
}

如果不编译该代码,我怎么知道设置的name变量是未包装的变量而不是传入的可选变量?

这是我在类似情况下在我的 Swift 代码中所做的:

func createNewPerson(name: String?) -> Person {
  let person = Person()
  if let unwrappedName = name {
    person.name = unwrappedName
  }
 return person
}

对我来说,只需将unwrapped添加到未包装的变量名称中,就可以使代码流更加清晰。 我们不必使用那种确切的语法(尤其是因为它使变量名变得更长),但我确实认为我们应该重新考虑隐藏变量。

好的,现在我已经提出了自己的理由:FIGHT FIGHT FIGHT FIGHT FIGHT!

1tpffru

所有22条评论

对我来说,在代码审查期间看到!是危险信号。 那时我开始问“为什么我们不选择性地绑定这个东西?”

我担心“未包装”对于书籍的前缀太长,将换行保持在最低限度是非常困难的,而且会占用大量水平空间。 就可读性而言,书中代码的换行可以说是更糟糕的 IMO。 当然,这个例子不会受到换行的影响,但是抛出一个 Cocoa API 调用,这将是一个不同的故事。

不过,我对阴影的立场可能会因情况而异,而且每天都会有所不同:]

如果不编译该代码,我怎么知道设置的 name 变量是未包装的变量,而不是传入的可选变量?

因为这就是 Swift 的工作方式。 如果您在if let范围内,则它使用未包装的版本。 这里没有歧义。

@hollance :对于熟悉 Swift 的人来说没有歧义,编译时也没有歧义。 对于不太了解它的人(例如:我)并试图在不检查和编译代码的情况下阅读代码,变量阴影使流程明显不那么明显。

@cwagdev也许前缀只是u而不是未包装? 在工作中,我们的 Android 代码标准要求作为参数传入的任何变量都必须a作为前缀,这是在清晰度和可变长度之间相当可靠的折衷方案。

我想这就是语义代码突出显示派上用场的地方。 ;-)

顺便说一句,你知道if var也适用于展开吗?

  if var name = name {
    name += ", Esq."
    person.name = name
  }

@hollance如果您要解包的可选项是本地属性,这当然会有所帮助,但如果它是传入的参数,则作用不大。

至于if varhttps://www.youtube.com/watch?v=Cd7tEsAuspA - 谢谢!

语义高亮与常规语法高亮的区别在于它为每个变量赋予了自己的颜色。 因此,您一眼就能看出展开后的name与参数name不同。

对此有一个 Xcode 插件,但我不知道它是否适用于 Swift。 https://github.com/kolinkrewinkel/Polychromatic

我强烈反对引入任何新的变量或常量前缀。 匈牙利符号几乎已经消亡,没有充分的理由复兴它!

我同意我们应该依靠开发工具来提供有关范围和上下文的提示。

我当然认为在编译环境中,这不是必需的——这就是为什么首先采用这条规则的原因。

问题是我们的代码经常在编译环境之外被阅读,无论是在网站上还是在书本上。 如果您尝试使用错误的非可选与可选varlet ,则此过程在没有编译器的帮助下会变得_significantly_不太明显。

@ColinEberhardt@hollance :您对我们如何在只读环境中为这些事物提供更好的上下文而不使用变量前缀有任何其他建议吗? 或者我们应该说,“去死吧,如果你不明白,就把它打到操场上。”?

+1 使用相同的名称。 一般来说,我会反对可变阴影,但在这种情况下,我相信这是有道理的。 它使事情变得更简单,因为不必使用创意名称或前缀。 同样,被遮蔽的变量实际上是指同一个变量,只是它的展开版本,即它并没有真正遮蔽任何其他变量,而是它本身的遮蔽。

我喜欢@jawwad的解释,即遮蔽实际上是“遮蔽自身”,因此使用相同的名称是有意义的。

你也可以争辩另一方:如果你有foounwrappedFoo然后你在unwrappedFoo上调用了一些方法,人们可能会混淆这是两个不同的东西并且foo没有被触及。 当他们理解可选时,他们最终会理解它,但阴影方式也是如此。

无论哪种方式,我们最终都需要依靠人们“得到它”,如果是这样的话——我宁愿优化人们理解选项的可能性。

让我们继续讨论吧! 也许我们可以超过关于 ivars 与属性的 Objective-C 风格指南讨论的帖子数!

一场来自上帝本人的潜在风暴, https://devforums.apple.com/message/1127586#1127586

我认为这篇文章更多的是关于变量阴影是否应该发出警告——莱特纳说不,并且没有关于你是否应该在风格上进行阴影的具体指导。

自从我引发了这场火灾,请允许我更新一下:我一直在使用 Swift 开发原型,但我仍然觉得在没有阴影的情况下阅读代码要困难得多。

对我来说归结为这个老栗子

Programs must be written for people to read, 
and only incidentally for machines to execute.

是的,当你实际编写代码时,编译器会告诉你什么时候搞砸了。 但是对于我来说,通过可变阴影阅读其他人(甚至我自己的)代码变得更加困难。 当我阅读阴影代码时,我很难直观地了解有人在访问未包装的值与包装的值。

我也一直在与新的程序员一起工作,他们在理解作用域时遇到了很多麻烦(我刚开始时确实这样做过),我只看到这个问题被变量遮蔽变得更糟了一千倍。 新程序员如何知道他们实际上是在访问特定范围内的未包装常量,而不是原始的可选变量?

在您正在展开的内容和易于阅读的展开的内容之间有一个清晰的界限将使新手更容易分辨他们正在访问的两者中的哪一个。

我知道这会让@ColinEberhardt非常难过,但我在我的个人代码中采用了u前缀,我发现它有很大帮助。

我不认为莱特纳在这里给出任何建议……他只是说不会有警告。

听起来他是在对我说“如果你真的愿意就去做”...... :]

同意,我主要是在讽刺,这个帖子让我想起了这个:微笑:

我仍然在影子阵营,但可以看到@designatednerd的观点。

@designatednerd阴影更整洁。 一个很容易推断的事实是,在 if let 范围内,您指的是未包装的版本。 显式解包是如此普遍,以至于任何有几天 Swift 经验的人都可能已经知道这一点。

@annehiro我不同意“任何值得几天使用 Swift 经验的人都可能已经知道这一点”的评估。 任何拥有 swift _and_ 大量其他编程经验的人都可能会得到它,因为他们了解范围界定。 我在上面详细介绍了为什么我认为这会让那些没有真正了解范围的 n00bs 更难理解。

但是, @gregheo ,我不知道这个问题是否值得再开放。 虽然我仍然觉得正如这个问题的标题所说(并且我仍然在我自己的代码中使用非阴影变量),但在站点级别进行此更改将需要在此时更新站点和书籍的大量工作。

这套代码标准被用作许多仍在开发自己的 Swift 代码标准(包括我的)的工作场所的参考,但最终,这些都是为了我们的网站和书籍。 而且我认为这匹马已经离开了谷仓,跑遍了半个国家,改变了教程和书籍。 :horse_racing:

FWIW 自从这件事发生以来,我已经写了很多 Swift,我会说我 90% 的时间都在遮蔽,当变量非常冗长时,我会跳过遮蔽。

这是一个艰难的问题,因为争论的双方都有好点。 就个人而言,我不喜欢“阴影”这个词,因为它实际上是从可选到非可选的静态类型转换。 我在阅读代码时从来没有遇到过麻烦,因为在呼叫站点很容易判断某些内容是可选的还是非可选的。 将其视为类型转换也与使用guard提前满足先决条件并在未满足时退出非常吻合。 我希望在未来的某一天,我们会得到如下所示的语法糖:

func compute(a: Int?, b: Int?) -> {
   unwrap a, b else { return }
   // compute with a and b Ints
}

Unwrap 也适用于神秘的Result类型。

很多时候,不使用相同的名称而是使用缩写形式是有意义的。 例如,来自 Siesta 框架(我认为非常棒的代码模数它有趣的空白):

guard let cache = config.persistentCache else { return }

其他时候它只是阴影。

我认为@cwagdev正在接受。 我检查的其他一些项目混合使用了阴影和非阴影,包括 Almofire 和 Apple 的 Foundation 开源实现。 然而,这些项目都没有使用匈牙利符号。

基于这些发现和我个人的直觉,我将裁定,目前不应该制定任何标准来影响或不影响。 关闭这个问题,但如果情况发生变化,我们可以重新审视它。

@rayfix 👍 我也打算结束这个问题。

然而,关于

我将规定,目前不应该制定任何标准来影响或不影响。

实际上,风格指南已经对此做出了裁决:

对于可选绑定,在适当的时候隐藏原始名称,而不是使用 unwrappedView 或 actualLabel 之类的名称。

当这个问题被打开时,并没有_很多我们的代码或教程使用“阴影”。

然而,现在情况不同了:

  • 我们已经在许多书面和视频教程中使用它。
  • 我们在 RWDevCon 2015 和 2016 上使用并解释了它。
  • 总而言之,我们基本上支持“隐藏”原始名称的想法。

在这一点上,我认为我们致力于它......就个人而言,我也越来越喜欢它......

就像好的🍷一样,它在你身上生长? 😉

对我来说,“不要使用匈牙利符号”,我完全同意。 “适当时”为以下内容提供了一点回旋余地:

guard let json = content as? NSDictionary else { throw InvalidJSON }

FWIW,我停止使用u并最终使用不同的变量名,但我确实意识到我已经输掉了这场战斗。 :微笑:

@designatednerd作为 Swift 的新手,我认为您在这方面是绝对正确的。 这对初学者来说很困惑。 我真的很困惑范围界定如何与此一起工作。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

hollance picture hollance  ·  28评论

samkim102 picture samkim102  ·  6评论

jrturton picture jrturton  ·  3评论

WingYn picture WingYn  ·  15评论

rayfix picture rayfix  ·  3评论