web-dev-qa-db-fra.com

Pourquoi les énumérations ont-elles des propriétés calculées mais pas des propriétés stockées dans Swift?

Je suis nouveau à Swift et je viens de découvrir ceci dans la documentation:

Les propriétés calculées sont fournies par les classes, les structures et énumérations. Les propriétés stockées sont fournies uniquement par les classes et structures.

Pourquoi donc? Les valeurs associées pour enum fonctionnent-elles comme des propriétés stockées? Il semble qu’ils aient initialement stocké des propriétés - Pourquoi aucune propriété de type stockée pour les classes dans swift?

22
Adit Gupta

enums ont stocké type properties - c'est-à-dire, static propriétés. Ils n'ont pas stocké instance properties. J'ignore s'il existe une raison technique pour laquelle les propriétés d'instance stockées ne sont pas disponibles pour enums. Vous devrez peut-être poser votre question sur le forum dev si vous voulez une réponse technique à "pourquoi".

Dans votre question, vous demandez si les valeurs associées fonctionnent comme des propriétés stockées. En fait, elles sont et sont plus souples (à certains égards) que les propriétés stockées pour structs et classes. Chaque case dans une enum peut avoir son propre ensemble de données spécialisé qui lui est associé. Plutôt que d'avoir un ensemble de propriétés stockées qui s'applique à toutes les cases, vous devez individualiser les propriétés stockées pour chaque case.

12
Aaron Rasmussen

L'énumération n'autorisant pas les propriétés d'instance stockée est un choix de conception. Avoir enum avec des propriétés d'instance stockées le fait ressembler à une structure (avec super pouvoirs), mais du point de vue type maintenant, les énumérations agiront comme des multiplicateurs de type. Fondamentalement considérer

enum Set1 {
    case a
    case b
    case c
}

enum Times {
    case x
    case y

    var k: Set1
}

Ce que cela signifie réellement que enum Times nous permet d’avoir une combinaison quelconque d’éléments de Set1 et Set2 entraînant 6 cas distincts, mais attendez, nous savons qu’il s’agit en fait d’un but de type Tuple comme (Set1, Set2), où Times peut être déclaré comme 

typealias Times = (Set1, Set2)

J'espère que cela constitue une justification raisonnable pour ne pas autoriser le premier cas. 

Cela étant dit, Swift Enums nous permet d’associer un n-Tuple arbitraire à chaque cas , nous permettant ainsi de déclarer ce que l’on appelle une union discriminée dans la programmation fonctionnelle. Appelez-le une propriété stockée attachée à la casse si vous le souhaitez. Du point de vue des types, il agit maintenant en tant que type additionneur. 

enum Add {
    case lhs(Set1)
    case rhs(Set2)
}

Nous avons maintenant 5 cas différents. Si nous stockons maintenant 2-tuples:

enum AddTimes {
    case lhs(Set1, Set2)
    case rhs(Set3, Set4)
}

nous avons maintenant essentiellement la somme de la multiplication (Set1 * Set2 + Set3 * Set4) . C'est un outil très puissant pour la recherche de motifs.

CEPENDANT, il existe des cas réels dans lesquels vous voulez réellement émuler la forme de la propriété stockée dans enum. Considère ceci:

public enum Service {
    case registerNewUser(username: String, password: String, language: String)
    case login(username: String, password: String, deviceTokenº: String?)
    case logout(sessionToken: String)
    case sendForgotPassword(email: String)
}

est une manière déclarative de définir les points de terminaison REST (dans un cadre tel que Moya ) Lorsque vous souhaitez déclencher une demande, vous devez faire quelque chose comme: 

MoyaProvider<Service>.request(.sendForgotPassword(email: "[email protected]"))

Mais maintenant, imaginez que vous souhaitiez faire la différence entre votre serveur de production et votre serveur de test. Si vous ajoutez un serveur en tant qu'autre élément Tuple dans chaque cas:

case forgotPassword(sessionToken: String, serverBaseURLString: String)

cela aura une mauvaise sémantique, puisque vous vouliez initialement que chaque Tuple stocke les paramètres de requête, mais maintenant il stocke une adresse de base du serveur.

Pour éviter de telles choses, nous pouvons paramétrer notre type de la manière suivante. Au lieu de définir Server comme suit:

enum Server: String {
    case production = "https://service.info"
    case test = "http://test.service.info"
}

nous pouvons le définir avec un type distinct pour chaque cas comme:

public struct ProductionServer: ServerType {
    public static var baseURLString: String { return "https://service.info" }
}
public struct TestServer: ServerType {
    public static var baseURLString: String { return  "http://test.service.info" }
}
public protocol ServerType {
    static var baseURLString: String { get }
}

et enfin paramétrer notre ServiceType en tant que 

public enum Service<T> where T: ServerType {
    case registerNewUser(username: String, password: String, language: String)
    case login(username: String, password: String, deviceTokenº: String?)
    case logout(sessionToken: String)
    case sendForgotPassword(email: String)

    var serverURL: URL {
        return T.baseURL
    }
}

public typealias ProdutionService = Service<ProductionServer>
public typealias TestService = Service<TestServer>
13
ambientlight

J'utilise un petit truc pour stocker des propriétés qui ne sont pas accessibles lors de l'initialisation. 

Tout d'abord, je crée une classe Future qui stockera la propriété stockée à l'avenir: 

class Future<T> {
  var value: T?
  init(value: T? = nil) {
      self.value = value
  }
}

Puis je crée mon enum: 

enum Sample {
  case Test(future: Future<String>)
}

Instancier:

let enumTest = Sample.Test(future: Future())

Plus tard dans le code: 

switch enumTest {
  case let .Test(future):
  future.value = "Foo"
}

Et plus tard, vous pouvez accéder à la valeur:

switch enumTest {
  case let .Test(future):
  // it will print Optional("Foo")
  print("\(future.value)")
}

Ce n'est pas quelque chose que vous devriez abuser, mais cela peut être utile dans certains cas. 

J'espère que ça aide.

4
manueGE

Une énumération est considérée comme un type de données structuré qui peut être modifié sans avoir à changer, disons une chaîne ou un entier plusieurs fois dans votre code. Avec une énumération, nous ne pouvons jamais avoir à nous soucier de changer la même chose plusieurs fois. Par exemple menu déroulant: 

enum DropDownMenuOptions: String {
  case MenuOptionOne
  case MenuOptionTwo
  case MenuOptionThree
}

Avec les propriétés stockées, vous pouvez précalculer les informations nécessaires et réduire le code dans votre fonction principale. Le meilleur exemple est le calcul de la taille de rect, par exemple:

struct Point {
    var x = 0.0, y = 0.0
}
struct Size {
    var width = 0.0, height = 0.0
}
struct Rect {
    var Origin = Point()
    var size = Size()
    var center: Point {
        get {
            let centerX = Origin.x + (size.width / 2)
            let centerY = Origin.y + (size.height / 2)
            return Point(x: centerX, y: centerY)
        }
        set(newCenter) {
            Origin.x = newCenter.x - (size.width / 2)
            Origin.y = newCenter.y - (size.height / 2)
        }
    }
}

var square = Rect(Origin: Point(x: 0.0, y: 0.0),
    size: Size(width: 10.0, height: 10.0))
0
Kristijan Delivuk