J'ai défini une énumération pour représenter une sélection d'une "station"; Les stations sont définies par un entier positif unique. J'ai donc créé l'énumération suivante afin de permettre aux valeurs négatives de représenter des sélections spéciales:
enum StationSelector : Printable {
case Nearest
case LastShown
case List
case Specific(Int)
func toInt() -> Int {
switch self {
case .Nearest:
return -1
case .LastShown:
return -2
case .List:
return -3
case .Specific(let stationNum):
return stationNum
}
}
static func fromInt(value:Int) -> StationSelector? {
if value > 0 {
return StationSelector.Specific(value)
}
switch value {
case -1:
return StationSelector.Nearest
case -2:
return StationSelector.LastShown
case -3:
return StationSelector.List
default:
return nil
}
}
var description: String {
get {
switch self {
case .Nearest:
return "Nearest Station"
case .LastShown:
return "Last Displayed Station"
case .List:
return "Station List"
case .Specific(let stationNumber):
return "Station #\(stationNumber)"
}
}
}
}
J'aimerais utiliser ces valeurs comme clés dans un dictionnaire. La déclaration d'un dictionnaire génère l'erreur attendue: StationSelector n'est pas conforme à Hashable. Se conformer à Hashable est facile avec une simple fonction de hachage:
var hashValue: Int {
get {
return self.toInt()
}
}
Cependant, Hashable
requiert la conformité à Equatable
et je n'arrive pas à définir l'opérateur égal à mon enum pour satisfaire le compilateur.
func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
return lhs.toInt() == rhs.toInt()
}
Le compilateur se plaint qu'il s'agit de deux déclarations sur une seule ligne et veut mettre un ;
après func
, ce qui n'a pas de sens non plus.
Des pensées?
Du livre Swift:
Les valeurs de membre d'énumération sans valeurs associées (comme décrit dans Enumérations) peuvent également être définies par défaut.
Cependant, votre énumération a une valeur de membre avec une valeur associée. La conformité Hashable
doit donc être ajoutée manuellement par vous.
Le problème de votre implémentation est que les déclarations d'opérateur dans Swift doivent avoir une portée globale.
Juste bouge:
func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
return lhs.toInt() == rhs.toInt()
}
en dehors de la définition enum
et cela fonctionnera.
Vérifiez la documentation pour plus d'informations à ce sujet.
J'ai eu du mal à essayer un peu de rendre une enum
avec les valeurs associées conforme à Hashable
.
Voici que j'ai rendu ma enum
avec les valeurs associées conforme à Hashable
afin qu'elle puisse être triée ou utilisée comme clé Dictionary
ou faire tout ce que Hashable
peut faire.
Vous devez rendre vos valeurs associées enum
conformes à Hashable
car les valeurs associées enums
ne peuvent pas avoir un type brut.
public enum Components: Hashable {
case None
case Year(Int?)
case Month(Int?)
case Week(Int?)
case Day(Int?)
case Hour(Int?)
case Minute(Int?)
case Second(Int?)
///The hashValue of the `Component` so we can conform to `Hashable` and be sorted.
public var hashValue : Int {
return self.toInt()
}
/// Return an 'Int' value for each `Component` type so `Component` can conform to `Hashable`
private func toInt() -> Int {
switch self {
case .None:
return -1
case .Year:
return 0
case .Month:
return 1
case .Week:
return 2
case .Day:
return 3
case .Hour:
return 4
case .Minute:
return 5
case .Second:
return 6
}
}
}
Il faut également remplacer l'opérateur d'égalité:
/// Override equality operator so Components Enum conforms to Hashable
public func == (lhs: Components, rhs: Components) -> Bool {
return lhs.toInt() == rhs.toInt()
}
Pour plus de lisibilité, réimplémentons StationSelector
avec Swift 3:
enum StationSelector {
case nearest, lastShown, list, specific(Int)
}
extension StationSelector: RawRepresentable {
typealias RawValue = Int
init?(rawValue: RawValue) {
switch rawValue {
case -1: self = .nearest
case -2: self = .lastShown
case -3: self = .list
case (let value) where value >= 0: self = .specific(value)
default: return nil
}
}
var rawValue: RawValue {
switch self {
case .nearest: return -1
case .lastShown: return -2
case .list: return -3
case .specific(let value) where value >= 0: return value
default: fatalError("StationSelector is not valid")
}
}
}
La référence API des développeurs Apple indique à propos de Hashable
protocol:
Lorsque vous définissez une énumération sans valeurs associées, elle acquiert automatiquement la conformité
Hashable
et vous pouvez ajouter la conformitéHashable
à vos autres types personnalisés en ajoutant une seule propriétéhashValue
.
Par conséquent, étant donné que StationSelector
implémente les valeurs associées, vous devez rendre StationSelector
conforme au protocole Hashable
manuellement.
La première étape consiste à implémenter l'opérateur ==
et à rendre StationSelector
conforme au protocole Equatable
:
extension StationSelector: Equatable {
static func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
return lhs.rawValue == rhs.rawValue
}
}
Utilisation:
let nearest = StationSelector.nearest
let lastShown = StationSelector.lastShown
let specific0 = StationSelector.specific(0)
// Requires == operator
print(nearest == lastShown) // prints false
print(nearest == specific0) // prints false
// Requires Equatable protocol conformance
let array = [nearest, lastShown, specific0]
print(array.contains(nearest)) // prints true
Une fois que le protocole Equatable
est mis en œuvre, vous pouvez adapter StationSelector
au protocole Hashable
:
extension StationSelector: Hashable {
var hashValue: Int {
return self.rawValue.hashValue
}
}
Utilisation:
// Requires Hashable protocol conformance
let dictionnary = [StationSelector.nearest: 5, StationSelector.lastShown: 10]
Le code suivant montre l'implémentation requise pour StationSelector
afin de la rendre conforme au protocole Hashable
à l'aide de Swift 3:
enum StationSelector: RawRepresentable, Hashable {
case nearest, lastShown, list, specific(Int)
typealias RawValue = Int
init?(rawValue: RawValue) {
switch rawValue {
case -1: self = .nearest
case -2: self = .lastShown
case -3: self = .list
case (let value) where value >= 0: self = .specific(value)
default: return nil
}
}
var rawValue: RawValue {
switch self {
case .nearest: return -1
case .lastShown: return -2
case .list: return -3
case .specific(let value) where value >= 0: return value
default: fatalError("StationSelector is not valid")
}
}
static func == (lhs: StationSelector, rhs: StationSelector) -> Bool {
return lhs.rawValue == rhs.rawValue
}
var hashValue: Int {
return self.rawValue.hashValue
}
}
Juste pour souligner ce que Cezar a dit auparavant. Si vous pouvez éviter d'avoir une variable membre, vous n'avez pas besoin d'implémenter l'opérateur equals pour rendre les énumérations hashable: donnez-leur simplement un type!
enum StationSelector : Int {
case Nearest = 1, LastShown, List, Specific
// automatically assigned to 1, 2, 3, 4
}
C'est tout ce dont vous avez besoin. Maintenant, vous pouvez également les initier avec la rawValue ou les récupérer plus tard.
let a: StationSelector? = StationSelector(rawValue: 2) // LastShown
let b: StationSelector = .LastShown
if(a == b)
{
print("Selectors are equal with value \(a?.rawValue)")
}
Pour plus d'informations, consultez la documentation .