OptionSetType和枚举 [英] OptionSetType and enums

查看:98
本文介绍了OptionSetType和枚举的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个名为 ProgrammingLanguage 的枚举:

enum ProgrammingLanguage {
  case Swift, Haskell, Scala
}

现在我有一个名为 Programmer 具有以下属性:

Now I have a class named Programmer with the following property:

let favouriteLanguages: ProgrammingLanguage = .Swift

看到程序员如何拥有几种喜欢的语言,我认为写起来会很不错像这样的东西:

Seeing how a programmer could have several favourite languages, I'd thought it'd be nice to write something like this:

let favouriteLanguages: ProgrammingLanguage = [.Swift, .Haskell]

经过一番研究,我意识到我需要遵循 OptionSetType ,但是在这样做会引发以下3个错误:

After a bit of research, I realized that I need to conform to OptionSetType, but in doing so, I've raise the following 3 errors:

ProgrammingLanguage不符合


  1. SetAlgebraType

  2. OptionSetType

  3. RawRepresentable

  1. SetAlgebraType
  2. OptionSetType
  3. RawRepresentable

当我看到 Raw Representable 错误时,我立即想到了枚举的关联类型。我一直希望能够打印枚举值,所以我将枚举签名更改为以下内容:

When I saw the Raw Representable error, I immediately thought of associated types for enums. I wanted to be able to print the enum value anyway, so I changed my enum signature to the following:

case ProgrammingLanguage: String, OptionSetType {
  case Swift, Haskell, Scala
}

此静音2警告。但是我还有一个问题,就是我不符合协议 SetAlgebraType

This silenced 2 of the warnings. But I'm still left with one which is that I don't conform to protocol SetAlgebraType.

经过一番尝试和错误后,我发现具有关联的枚举类型为 Int (这很有意义,因为 RawRepresentable 协议要求您实现签名 init(rawValue:Int)的初始化程序)。但是,我对此感到不满意;我希望能够轻松获得枚举的String表示形式。

After a bit of trial and error, I found out having the associated type of the enum as Int fixed it (which makes sense, since the RawRepresentable protocol requires you to implement an initializer of the signature init(rawValue: Int)). However, I'm unsatisfied with that; I want to be able to get the String representation of the enum easily.

有人可以建议我如何轻松地做到这一点,以及为什么 OptionSetType 需要 Int 关联类型?

Could someone advise me how I can do this easily, and why OptionSetType requires an Int associated type?

编辑:

以下声明可正确编译,但在运行时会出错:

The following declaration compiles correctly, but errors at runtime:

enum ProgrammingLanguage: Int, OptionSetType {
  case Swift, Scala, Haskell
}

extension ProgrammingLanguage {
  init(rawValue: Int) {
    self.init(rawValue: rawValue)
  }
}

let programmingLanguages: ProgrammingLanguage = [.Swift, .Scala]


推荐答案

编辑:我为以前的自我而感到惊讶,因为当时我没有提前说明,而是...而不是试图将其他值类型强制放入 OptionSet中协议(将Swift 3从名称中删除 Type ),最好在使用这些类型并使用在适当的地方设置集合

I'm surprised at my former self for not saying this upfront at the time, but... instead of trying to force other value types into the OptionSet protocol (Swift 3 removed Type from the name), it's probably better to consider the API where you use those types and use Set collections where appropriate.

OptionSet 类型很奇怪。它们都是集合,而不是集合-您可以从多个标志构造一个,但是结果仍然是单个值。 (您可以做一些工作来找出与该值等效的单个标志的集合,但是取决于类型中可能的值,它可能不是唯一的。)

OptionSet types are weird. They are both collections and not collections — you can construct one from multiple flags, but the result is still a single value. (You can do some work to figure out a collection-of-single-flags equivalent to such a value, but depending on the possible values in the type it might not be unique.)

另一方面,拥有一个 something 或多个唯一的 somethings 对API的设计很重要。您是要用户说自己拥有多个收藏夹,还是要说只有一个收藏夹?您要允许多少个收藏夹?如果用户要求多个收藏夹,是否应该按照用户特定的顺序对其进行排名?这些都是使用 OptionSet 样式类型很难回答的问题,但是如果使用 Set 则要容易得多类型或其他实际集合。

On the other hand, being able to have one something, or more than one unique somethings, can be important to the design of an API. Do you want users to say they have more than one favorite, or enforce that there's only one? Just how many "favorites" do you want to allow? If a user claims multiple favorites, should they be ranked in user-specific order? These are all questions that are hard to answer in an OptionSet-style type, but much easier if you use a Set type or other actual collection.

此答案的其余部分a)很旧,使用Swift 2名称,b)假定您正在尝试实现 OptionSet 无论如何,即使这对您的API来说是一个错误的选择...

The rest of this answer a) is old, using Swift 2 names, and b) assumes that you're trying to implement OptionSet anyway, even if it's a bad choice for your API...

请参阅关于 OptionSetType


符合 SetAlgebraType 对于其 RawValue BitwiseOperationsType 的任何类型。

Supplies convenient conformance to SetAlgebraType for any type whose RawValue is a BitwiseOperationsType.

换句话说,对于任何也采用 RawRepresentable OptionSetType 一致性>。但是,只有当您关联的原始值类型是符合 ArrayLiteralConvertible 一致性)。 > BitwiseOperationsType 。

In other words, you can declare OptionSetType conformance for any type that also adopts RawRepresentable. However, you gain the magic set-algebra syntax support (via operators and ArrayLiteralConvertible conformance) if and only if your associated raw value type is one that conforms to BitwiseOperationsType.

因此,如果原始值类型为 String ,则运气不好-您不会获得固定的代数知识,因为 String 不支持按位运算。 (这里的有趣之处是,您可以扩展 String 以支持 BitwiseOperationsType ,如果您的实现满足,则可以使用字符串作为选项集的原始值。)

So, if your raw value type is String, you're out of luck — you don't gain the set algebra stuff because String doesn't support bitwise operations. (The "fun" thing here, if you can call it that, is that you can extend String to support BitwiseOperationsType, and if your implementation satisfies the axioms, you can use strings as raw values for an option set.)

在运行时遇到第二个语法错误,因为您创建了无限递归-调用 self.init (rawValue:)来自 init(rawValue:)保持锣状直到炸毁堆栈。

Your second syntax errors at runtime because you've created an infinite recursion — calling self.init(rawValue:) from init(rawValue:) keeps gong until it blows the stack.

可以说是一个错误(请将其归档),您甚至可以尝试而不会出现编译时错误。枚举不能能够声明 OptionSetType 一致性,因为:

It's arguably a bug (please file it) that you can even try that without a compile time error. Enums shouldn't be able to declare OptionSetType conformance, because:


  1. 枚举的语义约定是封闭集。通过声明您的 ProgrammingLanguage 枚举,您是说类型 ProgrammingLanguage 的值必须是 Swift , Scala Haskell ,什么都没有。

  1. The semantic contract of an enum is that it's a closed set. By declaring your ProgrammingLanguage enum you're saying that a value of type ProgrammingLanguage must be one of Swift, Scala, or Haskell, and not anything else. A value of "Swift and Scala" isn't in that set.

OptionSetType 基于整数位字段。 Swift and Haskell值( [。Swift,.Haskell] )实际上只是 .Swift.rawValue | .Haskell.rawValue 。如果您的原始值集未按位对齐,则会造成麻烦。也就是说,如果 .Swift.rawValue == 1 == 0b01 .Haskell.rawValue == 2 == 0b10 ,其中的按位或为 0b11 == 3 ,与 .Scala.rawValue 相同。

The underlying implementation of an OptionSetType is based on integer bitfields. A "Swift and Haskell" value, ([.Swift, .Haskell]) is really just .Swift.rawValue | .Haskell.rawValue. This causes trouble if your set of raw values isn't bit-aligned. That is, if .Swift.rawValue == 1 == 0b01, and .Haskell.rawValue == 2 == 0b10, the bitwise-or of those is 0b11 == 3, which is the same as .Scala.rawValue.



TLDR:如果要符合 OptionSetType ,请声明



并使用 static let 声明您类型的成员。

TLDR: if you want OptionSetType conformance, declare a struct.

And use static let to declare members of your type.

并选择原始值,以使您想要与其他成员的可能(按位或)组合不同的成员实际上是。

And pick your raw values such that members you want to be distinct from possible (bitwise-or) combinations of other members actually are.

struct ProgrammingLanguage: OptionSetType {
    let rawValue: Int

    // this initializer is required, but it's also automatically
    // synthesized if `rawValue` is the only member, so writing it
    // here is optional:
    init(rawValue: Int) { self.rawValue = rawValue }

    static let Swift    = ProgrammingLanguage(rawValue: 0b001)
    static let Haskell  = ProgrammingLanguage(rawValue: 0b010)
    static let Scala    = ProgrammingLanguage(rawValue: 0b100)
}

保持值与众不同的好方法:使用上述二进制文字语法,或声明位移为1的值,如下所示:

Good ways to keep your values distinct: use binary-literal syntax as above, or declare your values with bit shifts of one, as below:

    static let Swift    = ProgrammingLanguage(rawValue: 1 << 0)
    static let Haskell  = ProgrammingLanguage(rawValue: 1 << 1)
    static let Scala    = ProgrammingLanguage(rawValue: 1 << 2)

这篇关于OptionSetType和枚举的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆