Swift常数:Struct或Enum


78

我不确定两者中哪个最好定义常量。结构或枚举。每次我使用与否,都会复制一个结构吗?当我想到带有static let常量的结构时,我认为它会一直复制是没有意义的。但是,如果它不会被复制,那我拿走什么都没关系?

选择结构或枚举有什么优势?

弗朗西斯科说使用Struct的。

雷·温德利希说要用枚举。但是我缺乏理由。


3
在链接的文章中有一个理由:“使用无大小写的枚举的优点是它不会被意外实例化,并且可以用作纯名称空间。”
Martin R

好的,听起来很合逻辑。因此,我应该在90%的情况下使用枚举。一旦需要实例化或将其更改为变量,就使用结构。正确?
Paixsn

2
为什么不在使用它们的类中定义它们?为什么需要将所有常量放在一个结构中?如果使用扩展名,您仍然可以将它们保存在一个文件中。如果要在枚举和结构之间做出选择,那么从体系结构的角度来看,我都不会说。
苏珊(Sulthan)2016年

因为我需要一个可以包含在我的大部分项目中的框架。我将在所有它们中都需要相同的常量。所以我不想多次写。
Paixsn

1
@SnowN我不是反对常量,但是我告诉你,如果它们没有共同点,则无需将它们全部放入一个共同的结构/枚举中。
苏珊(Sulthan)2016年

Answers:


128

结构和枚举均起作用。例如,两者

struct PhysicalConstants {
    static let speedOfLight = 299_792_458
    // ...
}

enum PhysicalConstants {
    static let speedOfLight = 299_792_458
    // ...
}

工作并定义静态属性PhysicalConstants.speedOfLight

回复:每次使用或不使用结构都会被复制吗?

这两个structenum是值类型,以便将适用于枚举为好。但这无关紧要,因为您根本不必创建值:静态属性(也称为类型属性)是类型本身的属性,而不是该类型的实例。

回复:选择结构或枚举有什么优势?

链接文章所述

使用不区分大小写的枚举的优点是它不会被意外实例化并且可以用作纯名称空间。

所以对于一个结构,

let foo = PhysicalConstants()

创建一个type的(无用)值PhysicalConstants,但是对于一个不区分大小写的枚举,它将无法编译:

let foo = PhysicalConstants()
// error: 'PhysicalConstants' cannot be constructed because it has no accessible initializers

在涉及switch语句的一种特殊情况下,您不能使用无大小写的枚举。请参阅我的答案以获取讨论和可能的解决方法。
蒂姆·富夸

6
较小的补充:如果我们添加private init() {}Struct示例中,它还将具有无法即时实例化的“优势”。(自然地,某些开发人员可以通过在:的扩展中包括一个初始化程序来规避此“优势”,Struct但是如果出于某种原因,我们更愿意以Struct“纯名称空间”的方式使用a ,而不是使用enum,那么私有初始化程序可能是一个很好的实例,不要用作实例保护措施)。
dfrib

...我只是意识到这一点在下面的另一个答案中有所提及(尽管提到的同文件私有问题在Swift 3中不再存在)。
dfrib

这是一个非常无用的优势。我实际上是来这里struct Constants static let speedOfLight = 300enum Constants enum Light : Int case speed = 300进行比较的。您也可以比较一下答案中的内容吗?还是该比较有另一个答案?
亲爱的

@Honey:这是一个不同的问题。据我了解,这是关于提供名称空间的。静态常量可以在结构内部或枚举内部定义。它们可以具有不同的类型,并且您可以具有相同值的多个常量。–枚举的情况定义了相同类型的值(相互不同)。
Martin R

16

这是一个简短的答案:您的常数需要唯一吗?然后使用一个枚举,强制执行此操作。

是否想使用几个不同的常数来包含相同的值(通常为清楚起见很有用)?然后使用一个结构,它允许这样做。


1
我认为他从那以后就不会再使用个别案例了,他总是需要写信StaticVars.pi.rawValue
Michael

7

两者之间的区别是可以实例化结构,而枚举则不能。因此,在大多数只需要使用常量的情况下,最好使用枚举来避免混淆。

例如:

struct Constants {
    static let someValue = "someValue"
}

let _ = Constants()

上面的代码仍然有效。

如果我们使用一个枚举:

enum Constants {
    static let someValue = "someValue"
}

let _ = Constants() // error

以上代码无效,因此避免混淆。


6

使用Xcode 7.3.1和Swift 2.2

虽然我同意Martin R的观点,但Ray Wenderlich样式指南指出,由于枚举是纯名称空间,因此在几乎所有用例中枚举都更好,这是一个好地方,但在某个地方使用struct王牌enums

切换语句

让我们从struct版本开始:

struct StaticVars {
    static let someString = "someString"
}

switch "someString" {
case StaticVars.someString: print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}

使用结构,它将匹配并打印出 Matched StaticVars.someString

现在,让我们考虑无大小写的枚举版本(只需将关键字更改structenum):

enum StaticVars {
    static let someString = "someString"
}

switch "someString" {
case StaticVars.someString: print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}

您会注意到,case StaticVars.someString:在线的switch语句中出现编译时错误。错误是Enum case 'someString' not found in type 'String'

通过将static属性转换为返回类型的闭包,可以找到一种伪解决方法。

因此,您可以这样更改它:

enum StaticVars {
    static let someString = { return "someString" }
}

switch "someString" {
case StaticVars.someString(): print("Matched StaticVars.someString")
default: print("Didn't match StaticVars.someString")
}

注意case语句中需要括号,因为它现在是一个函数。

不利的一面是,既然我们已经使它成为一个函数,则每次调用它都会被执行。因此,如果它只是一个简单的原始类型,例如StringInt,那么还不错。它本质上是一个计算属性。如果它是一个需要计算的常数,而您只想计算一次,请考虑将其计算为其他属性,然后在闭包中返回该已计算的值。

您还可以使用专用的初始化器覆盖默认的初始化器,然后获得与无case枚举相同的编译时错误优度。

struct StaticVars {
    static let someString = "someString"
    private init() {}
}

但是,与此相关的是,您希望将结构的声明放入其自己的文件中,因为如果您将其声明与一个View Controller类放在同一文件中,则该类的文件仍然能够意外地实例化一个无用的文件。的实例StaticVars,但在类文件的外部,它将按预期工作。但这是你的电话。


3
显然,此问题已得到解决。在“无壳枚举版”编译和运行为8的测试版预计6在Xcode
马丁- [R

1
对!我真的很喜欢使用无用枚举的枚举来获得“无实例化”的好处。而且我也很高兴我以Xcode版本信息开始我的文章,否则可能存在“在我的机器上工作”的问题。
蒂姆·富夸

@MartinR正如您指出的那样,在Xcode 8中,对“无基枚举”的情况进行了排序,因此,现在在“ struct”和“ enum”中声明static let有什么区别。
G.Abhisek

@ G.Abhisek:我试图在答案中回答。使用无用的枚举会阻止您创建该类型的(无用)实例。对于常量本身,它根本没有任何区别。
马丁R

@MartinR因此,这意味着当我们通过结构访问时,我们不必要地创建了一个实例,而在枚举的情况下,它充当了命名空间。
G.Abhisek '16

1

Combine框架中,Apple选择了对名称空间使用枚举。

enum Publishers

充当发布者的类型的名称空间。

enum Subscribers

用作订阅者的类型的名称空间。

enum Subscriptions

与订阅相关的符号的名称空间。

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.