OptionSetType und Enumerationen
Habe ich eine Enumeration namens ProgrammingLanguage
:
enum ProgrammingLanguage {
case Swift, Haskell, Scala
}
Nun habe ich eine Klasse namens Programmer
mit der folgenden Eigenschaft:
let favouriteLanguages: ProgrammingLanguage = .Swift
Sehen, wie ein Programmierer könnte mehrere Lieblings-Sprachen, hatte ich gedacht, es wäre schön, etwas zu schreiben wie diese:
let favouriteLanguages: ProgrammingLanguage = [.Swift, .Haskell]
Nach ein bisschen recherche habe ich gemerkt, dass ich zu entsprechen OptionSetType
, aber damit habe ich raise die folgenden 3 Fehler:
ProgrammingLanguage nicht entsprechen,
SetAlgebraType
OptionSetType
RawRepresentable
Als ich sah, das Raw Darstellbar Fehler, ich dachte sofort an assoziierten Typen für Enumerationen. Ich wollte in der Lage sein zu drucken, die enum-Wert sowieso nicht, also änderte ich meine enum-Signatur wie folgt:
case ProgrammingLanguage: String, OptionSetType {
case Swift, Haskell, Scala
}
Diese zum schweigen gebracht 2 Warnungen. Aber ich bin immer noch Links mit ein, die ist, dass ich nicht entsprechen-Protokoll SetAlgebraType
.
Nach ein bisschen von Versuch und Irrtum, fand ich heraus, dass der zugehörige Typ der enum als Int
es behoben (was Sinn macht, da die RawRepresentable
- Protokoll erfordert die Implementierung einer Initialisierung der Signatur init(rawValue: Int)
). Allerdings bin ich unzufrieden mit diesem, ich möchte in der Lage sein, um die String-Darstellung der enum-leicht.
Könnte jemand mir raten, wie kann ich dies leicht tun, und warum OptionSetType
erfordert eine Int
Zusammenhang geben?
Edit:
Folgende Erklärung richtig kompiliert, aber Fehler zur Laufzeit:
enum ProgrammingLanguage: Int, OptionSetType {
case Swift, Scala, Haskell
}
extension ProgrammingLanguage {
init(rawValue: Int) {
self.init(rawValue: rawValue)
}
}
let programmingLanguages: ProgrammingLanguage = [.Swift, .Scala]
- Ich bezweifle, dass Sie kombinieren können
enum
undOptionSetType
weil beiderawValue
's sich gegenseitig stören. - Es ist wirklich interessant, dass es richtig kompiliert. Beim ausführen stürzt die app auf die Zeile, die versucht, die init-option, mit der debugger zeigt die init aufgerufen 31970 mal vor dem Absturz.
- Ich denke, der code läuft in einer endlos-Schleife (welche ist egal, zur compile-Zeit) oder etwas ähnliches, und dann stürzt auf einem buffer overflow.
- Ich glaube, du verwechselst
associated values
mitraw values
: developer.apple.com/library/content/documentation/Swift/... - im Zusammenhang twitter.com/johnsundell/status/906097785883242496
Du musst angemeldet sein, um einen Kommentar abzugeben.
Edit: ich bin überrascht, mein altes selbst, um nicht zu sagen, dies offenherzig zu der Zeit, aber..., anstatt zu versuchen zu zwingen, andere Werttypen in der
OptionSet
Protokoll (Swift 3 entferntType
aus dem Namen), ist es wahrscheinlich besser zu betrachten, die API bei der Verwendung dieser Arten und VerwendungSet
Sammlungen, wo angebracht.OptionSet
Typen sind komisch. Sie sind beide Sammlungen und Sammlungen nicht — Sie konstruieren kann man aus mehreren Flaggen, aber das Ergebnis ist immer noch single Wert. (Sie können einige Arbeit, um herauszufinden, eine Sammlung von single-flags entspricht solch ein Wert, sondern je nach der möglichen Werte in der Art, es könnte nicht eindeutig sein.)Auf der anderen Seite, dass in der Lage, eine etwas, oder mehr als eine einzigartige jährigen, kann wichtig sein, um das design einer API. Möchten Sie Benutzer zu sagen, Sie haben mehr als ein Lieblings-oder durchzusetzen, dass es nur eine? Wie viele "Favoriten" möchten Sie zulassen? Wenn ein Benutzer behauptet, mehrere Favoriten, sollte Sie eingestuft werden, in Benutzer-spezifischen um? All das sind Fragen, die schwer zu beantworten in einer
OptionSet
-Stil-Typ, aber viel einfacher, wenn Sie eineSet
- Typ oder anderen aktuellen Kollektion.Den rest dieser Antwort a) ist alt, mit Swift 2 Namen, und b) davon ausgegangen, dass Sie versuchen, zu implementieren
OptionSet
trotzdem, auch wenn es eine schlechte Wahl für Ihre API...Sehen die docs für
OptionSetType
:In anderen Worten, können Sie erklären
OptionSetType
übereinstimmung für jeden Typ, der nimmt auchRawRepresentable
. Aber Sie gewinnen die magic set-algebra-syntax-Unterstützung (über Operatoren undArrayLiteralConvertible
conformance), wenn und nur wenn Ihre zugehörigen raw-Wert Typ ist, entsprichtBitwiseOperationsType
.Also, wenn Sie Ihre raw-Wert Typ ist
String
, sind Sie aus Glück heraus — Sie profitieren nicht von der set-algebra Zeug, weilString
keine Unterstützung für bitweise Operationen. (Der "Spaß", was hier, wenn man das so nennen kann, ist, dass Sie können erweiternString
zu unterstützenBitwiseOperationsType
, und wenn Ihre Umsetzung erfüllt die Axiome, können Sie Zeichenfolgen verwenden, die als raw-Werte für eine option gesetzt werden.)Ihre zweite syntax-Fehler zur Laufzeit, weil Sie erstellt haben, eine unendliche Rekursion — Aufruf
self.init(rawValue:)
ausinit(rawValue:)
hält den gong, bis es weht der stack.Es ist wohl ein bug ( bitte Datei ), dass Sie können sogar versuchen, ohne eine compile-Zeit-Fehler. Enums sollte nicht in der Lage sein, zu erklären
OptionSetType
übereinstimmung, denn:Die semantische Vertrag von enum ist, dass es eine geschlossene Gruppe. Durch die Erklärung Ihrer
ProgrammingLanguage
enum du sagst, dass ein Wert vom TypProgrammingLanguage
muss manSwift
,Scala
oderHaskell
, und nichts anderes. Ein Wert von "Swift und die Scala" ist nicht in diesem Satz.Der zugrunde liegenden Implementierung eines
OptionSetType
basiert auf integer-bitfields. Eine "schnelle und Haskell" - Wert ([.Swift, .Haskell]
) ist wirklich nur.Swift.rawValue | .Haskell.rawValue
. Dies verursacht Probleme, wenn deine raw-Werte sind nicht bit-ausgerichtet. Das ist, wenn.Swift.rawValue == 1 == 0b01
, und.Haskell.rawValue == 2 == 0b10
des bitweisen-oder ist0b11 == 3
, das ist das gleiche wie.Scala.rawValue
.TLDR: wenn Sie wollen
OptionSetType
Konformität, deklarieren Sie ein struct.Und verwenden
static let
zu erklären die Mitglieder Ihr geben.Und wählen Sie Ihre raw-Werte so, dass die Mitglieder Sie möchten, um zu werden, deutlich von möglich (Bitweises oder) Kombinationen von anderen Mitgliedern tatsächlich sind.
Gute Möglichkeiten, um Ihre Werte verschiedene: binäre-literal-syntax wie oben, oder erklären Sie Ihre Werte mit bit-Verschiebungen um ein, wie unten:
init(rawValue: )
es kann hier weggelassen.init(rawValue:)
kostenlos iff Ihre einzige Instanz-Eigenschaft istrawValue
. Aber wenn Sie hinzufügen mehr Eigenschaften, Sie verlieren protocol Zustimmungen, die davon abhängen, dass eine Initialisierung genauinit(rawValue:)
. Ich finde es hilfreich, um es dort sowieso — es schützt gegen solche wichtige änderungen in der Zukunft, und es macht die Protokoll-Konformität eindeutiger.String
zu unterstützenBitwiseOperationsType
" Es würde das Leben viel einfacher...BitwiseOperations
Protokoll (Swift 3 fielType
aus dem Namen). Sie brauchen eine^
operator, der zwei strings und gibt einen Wert zurück, die behandelt werden kann als die symmetrische Differenz zwischen Ihnen; z.B.a ^ a == .allZeroes
unda ^ .allZeroes == a
. Und so weiter für die anderen Operatoren.Ich denke, man könnte einfach erreichen es in der modernen Art und Weise {^_^}.
... dann
Referenz: https://nshipster.com/optionset/