Swift 常量:结构或枚举

Posted

技术标签:

【中文标题】Swift 常量:结构或枚举【英文标题】:Swift constants: Struct or Enum 【发布时间】:2016-11-29 20:01:02 【问题描述】:

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

选择结构体或枚举有什么好处?

Francisco say use Struct's.

Ray Wenderlich say use Enum's. But I lack the justification.

【问题讨论】:

链接文章中有一个理由:“使用无大小写枚举的优点是它不会被意外实例化并且可以作为纯命名空间工作。” 好吧,这听起来合乎逻辑。所以我应该在 90% 的情况下使用枚举。一旦需要实例化或可变的东西,我就会使用结构。对吗? 为什么不在使用它们的类中定义它们呢?为什么需要将所有常量放入一个结构中?如果您使用扩展名,您仍然可以将它们放在一个文件中。如果您在 enum 和 struct 之间做出决定,我不会从架构的角度说。 因为我需要一个可以包含在大部分项目中的框架。我将在所有这些中都需要相同的常量。所以我不想写多次。 @SnowN 我不反对常量,但我告诉你,如果它们没有任何共同点,就没有必要将它们全部放入一个公共结构/枚举中。 【参考方案1】:

结构和枚举都有效。例如,两个

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

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

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

Re: 每次使用时都会复制一个结构体?

structenum 都是值类型,因此也适用于枚举。但这在此处无关 因为您根本不必创造价值: 静态属性(也称为 type 属性)是类型本身的属性,而不是该类型实例的属性。

Re:选择结构体或枚举有什么好处?

如linked-to article中提到的:

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

所以对于一个结构,

let foo = PhysicalConstants()

创建PhysicalConstants 类型的(无用的)值,但是 对于没有大小写的枚举,它无法编译:

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

【讨论】:

有一种特殊情况涉及 switch 语句,您不能使用无大小写枚举。请参阅我的答案以进行讨论和可能的解决方法。 次要添加:如果我们将private init() 添加到Struct 示例中,它还将具有无法意外即时实例化的“优势”。 (当然,某些开发用户可以通过在 Struct 的扩展中包含初始化程序来规避这种“优势”:但是 if 我们出于某种原因更喜欢使用 Struct一个“纯命名空间”方式,而不是 enum,那么私有初始化程序可能是一个很好的不使用实例安全保护)。 ... 我刚刚意识到这一点(有点)在下面的另一个答案中提到(尽管提到的同文件私人问题在 Swift 3 中不再存在)。跨度> 这是一个非常无用的优势。实际上,我来这里是为了比较struct Constants static let speedOfLight = 300enum Constants enum Light : Int case speed = 300 你能比较一下你的答案吗?或者这种比较还有其他答案吗? @Honey:这是一个不同的问题。这个(据我了解)是关于提供 namespace。 静态常量可以在结构内或枚举内定义。它们可以有不同的类型,并且您可以有多个具有相同值的常量。 – 枚举的cases定义了相同类型的(相互不同的)值。【参考方案2】:

这里有一个简短的回答:你的常量需要是唯一的吗?然后使用枚举来强制执行此操作。

想要使用几个不同的常量来包含相同的值(通常有助于清晰)?然后使用允许这样做的结构。

【讨论】:

我认为他不会使用个案,以后他总是需要写StaticVars.pi.rawValue【参考方案3】:

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

例如:

struct Constants 
    static let someValue = "someValue"


let _ = Constants()

上面的代码仍然有效。

如果我们使用枚举:

enum Constants 
    static let someValue = "someValue"


let _ = Constants() // error

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

【讨论】:

据我所知,如果您将其所有 init 方法显式设为私有,则无法实例化结构【参考方案4】:

使用 Xcode 7.3.1 和 Swift 2.2

虽然我同意 Martin R 的观点,并且 Ray Wenderlich 风格指南提出了一个很好的观点,即枚举在几乎所有用例中都更好,因为它是一个纯命名空间,但有一个地方使用 struct 胜过 @987654322 @。

切换语句

让我们从结构版本开始:

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

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

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'

有一个伪解决方法,将静态属性转换为返回类型的闭包。

所以你可以这样改变它:

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,这还不错。它本质上是一个计算属性。如果它是一个需要计算的常量并且您只想计算一次,请考虑将其计算到不同的属性中并在闭包中返回已经计算的值。

您还可以使用私有初始化器覆盖默认初始化器,然后您将获得与无大小写枚举相同的编译时错误优点。

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

但是有了这个,你想把结构的声明放在它自己的文件中,因为如果你在同一个文件中声明它,比如说,一个 View Controller 类,那个类的文件仍然可以不小心实例化一个无用的StaticVars 实例,但在类文件之外它会按预期工作。但这是你的决定。

【讨论】:

显然这个问题已经解决了。 “无壳枚举版本”在 Xcode 8 beta 6 中按预期编译和运行。 正确!我真的很喜欢使用无案例枚举来获得“无实例化”的好处。我也很高兴我以 Xcode 版本信息开始我的帖子,否则可能会出现“在我的机器上工作”的问题。 @MartinR 正如您所指出的,在 Xcode 8 中“无基础枚举”的情况已排序,那么现在在“struct”和“enum”中声明 static let 有什么区别。 @G.Abhisek:我试图在我的回答中回答这个问题。使用无大小写枚举会阻止您创建该类型的(无用)实例。对于常量本身,它根本没有区别。 @MartinR 这意味着当我们通过结构访问时,我们不必要地创建了一个实例,而在枚举的情况下,它充当了一个命名空间。【参考方案5】:

在Combine 框架中,Apple 选择优先使用枚举作为命名空间。

enum Publishers

作为发布者的类型的命名空间。

enum Subscribers

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

enum Subscriptions

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

【讨论】:

以上是关于Swift 常量:结构或枚举的主要内容,如果未能解决你的问题,请参考以下文章

Swift的闭包,枚举,类和结构体

通过扩展自动合成 Swift 结构或枚举的 Equatable 一致性

如何区分 Swift 3.0 中的类(确定它不是结构或枚举)[重复]

Swift - 1 (常量变量字符串数组字典元组循环枚举函数)

在 Swift 3.0 中的转义闭包中改变自我(结构/枚举)

常量_枚举_结构