web-dev-qa-db-fra.com

Pourquoi choisir Struct Over Class?

Jouant avec Swift, issu d'un environnement Java, pourquoi voudriez-vous choisir une structure plutôt qu'une classe? On dirait qu'ils sont la même chose, avec un Struct offrant moins de fonctionnalités. Pourquoi le choisir alors?

423
bluedevil2k

Selon le très populaire discours sur la programmation orientée protocole WWDC 2015 dans Swift ( video , transcript ), Swift fournit un certain nombre de fonctionnalités qui rendent les structures meilleures que les classes dans de nombreuses circonstances.

Les structures sont préférables si elles sont relativement petites et copiables, car la copie est beaucoup plus sûre que d'avoir plusieurs références à la même instance, comme c'est le cas avec les classes. Ceci est particulièrement important lorsque vous passez une variable à plusieurs classes et/ou dans un environnement multithread. Si vous pouvez toujours envoyer une copie de votre variable à d’autres endroits, vous n’aurez plus jamais à vous inquiéter du fait que cet endroit change la valeur de votre variable en dessous de vous.

Avec Structs, il est beaucoup moins nécessaire de s’inquiéter des fuites de mémoire ou des threads multiples qui accourent pour modifier une seule instance d’une variable. (Pour les plus avisés, l'exception à cette règle concerne la capture d'une structure à l'intérieur d'une fermeture, car elle capture en réalité une référence à l'instance, sauf si vous la marquez explicitement comme étant copiée).

Les classes peuvent également devenir saturées, car une classe ne peut hériter que d'une seule super-classe. Cela nous encourage à créer d’énormes super-classes englobant de nombreuses capacités différentes qui ne sont que vaguement liées. L'utilisation de protocoles, en particulier avec des extensions de protocole où vous pouvez fournir des implémentations de protocoles, vous évite d'avoir à utiliser des classes pour obtenir ce type de comportement.

La discussion présente ces scénarios dans lesquels les classes sont préférées:

  • Copier ou comparer des instances n'a pas de sens (par exemple, Window)
  • La durée de vie d'une instance est liée à des effets externes (par exemple, TemporaryFile).
  • Les instances ne sont que des "puits" - des conduites en écriture seule vers un état externe (par exemple, CGContext)

Cela implique que struct devrait être la valeur par défaut et que les classes devraient être une solution de secours.

D'autre part, Le langage de programmation Swift la documentation est quelque peu contradictoire:

Les instances de structure sont toujours passées par valeur et classe les instances sont toujours passées par référence. Cela signifie qu'ils sont adapté à différents types de tâches. En considérant les données Constructions et fonctionnalités dont vous avez besoin pour un projet, décidez si chaque construction de données doit être définie en tant que classe ou en tant que structure.

En règle générale, envisagez de créer une structure quand un ou plusieurs de ces conditions s'appliquent:

  • L’objectif principal de la structure est d’encapsuler quelques valeurs de données relativement simples.
  • Il est raisonnable de s’attendre à ce que les valeurs encapsulées soient copiées plutôt que référencées lorsque vous affectez ou transmettez un fichier exemple de cette structure.
  • Toutes les propriétés stockées par la structure sont elles-mêmes des types de valeur, qui devraient également être copiées plutôt que référencées.
  • La structure n'a pas besoin d'hériter des propriétés ou du comportement d'un autre type existant.

Voici des exemples de bons candidats pour les structures:

  • La taille d'une forme géométrique, encapsulant éventuellement une propriété width et une propriété height, toutes deux de type Double.
  • Un moyen de faire référence à des plages dans une série, encapsulant éventuellement une propriété de départ et une propriété de longueur, de type Int.
  • Un point dans un système de coordonnées 3D, encapsulant éventuellement des propriétés x, y et z, chacune de type Double.

Dans tous les autres cas, définissez une classe et créez des instances de cette classe être géré et passé par référence. En pratique, cela signifie que la plupart des constructions de données personnalisées doivent être des classes et non des structures.

Ici, il affirme que nous devrions utiliser par défaut des classes et des structures uniquement dans des circonstances spécifiques. En fin de compte, vous devez comprendre l’implication réelle des types de valeur par rapport aux types de référence, puis prendre une décision éclairée quant au moment d’utiliser des structures ou des classes. N'oubliez pas non plus que ces concepts sont en constante évolution et que la documentation de Swift Programming Language a été écrite avant le discours sur la programmation orientée protocole.

499
drewag

Puisque les instances de structure sont allouées sur la pile et que les instances de classe sont allouées sur le tas, les structures peuvent parfois être considérablement plus rapides.

Cependant, vous devez toujours le mesurer vous-même et décider en fonction de votre cas d'utilisation unique.

Prenons l'exemple suivant, qui illustre deux stratégies d'encapsulation du type de données Int à l'aide de struct et class. J'utilise 10 valeurs répétées pour mieux refléter le monde réel, où vous avez plusieurs champs.

class Int10Class {
    let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
    init(_ val: Int) {
        self.value1 = val
        self.value2 = val
        self.value3 = val
        self.value4 = val
        self.value5 = val
        self.value6 = val
        self.value7 = val
        self.value8 = val
        self.value9 = val
        self.value10 = val
    }
}

struct Int10Struct {
    let value1, value2, value3, value4, value5, value6, value7, value8, value9, value10: Int
    init(_ val: Int) {
        self.value1 = val
        self.value2 = val
        self.value3 = val
        self.value4 = val
        self.value5 = val
        self.value6 = val
        self.value7 = val
        self.value8 = val
        self.value9 = val
        self.value10 = val
    }
}

func + (x: Int10Class, y: Int10Class) -> Int10Class {
    return IntClass(x.value + y.value)
}

func + (x: Int10Struct, y: Int10Struct) -> Int10Struct {
    return IntStruct(x.value + y.value)
}

La performance est mesurée en utilisant

// Measure Int10Class
measure("class (10 fields)") {
    var x = Int10Class(0)
    for _ in 1...10000000 {
        x = x + Int10Class(1)
    }
}

// Measure Int10Struct
measure("struct (10 fields)") {
    var y = Int10Struct(0)
    for _ in 1...10000000 {
        y = y + Int10Struct(1)
    }
}

func measure(name: String, @noescape block: () -> ()) {
    let t0 = CACurrentMediaTime()

    block()

    let dt = CACurrentMediaTime() - t0
    print("\(name) -> \(dt)")
}

Le code peut être trouvé à https://github.com/knguyen2708/StructVsClassPerformance

UPDATE (27 mars 2018):

À partir de Swift 4.0, Xcode 9.2, la version finale en cours d’exécution sur iPhone 6S, iOS 11.2.6, le paramètre du compilateur Swift est -O -whole-module-optimization:

  • class version a pris 2,06 secondes
  • La version struct a pris 4.17e-08 secondes (50 000 000 fois plus rapide)

(Je ne fais plus la moyenne des exécutions multiples, car les variances sont très petites, moins de 5%)

Note: la différence est beaucoup moins spectaculaire sans optimisation du module entier. Je serais heureux si quelqu'un peut indiquer ce que le drapeau fait réellement.


UPDATE (7 mai 2016):

À partir de Swift 2.2.1, Xcode 7.3, en cours de publication de la version finale sur iPhone 6S, iOS 9.3.1, calculé en moyenne sur 5 exécutions, le paramètre Swift Compiler est défini sur -O -whole-module-optimization:

  • class version a pris 2.159942142s
  • La version struct a pris 5.83E-08s (37 000 000 fois plus rapide)

Note: comme quelqu'un l'a mentionné, dans le monde réel, il y aura probablement plus d'un champ dans une structure. J'ai ajouté des tests pour les structures/classes comportant 10 champs au lieu de 1. Étonnamment, les résultats ne varient pas. beaucoup.


RÉSULTATS ORIGINAUX (1er juin 2014):

(Couru sur struct/classe avec 1 champ, pas 10)

À partir de Swift 1.2, Xcode 6.3.2, exécutant la version finale sur iPhone 5S, iOS 8.3, calculée en moyenne sur 5 exécutions.

  • La version class a pris 9,788332333s
  • La version struct a pris 0.010532942s (900 fois plus rapide)

ANCIENS RÉSULTATS (à partir d'une heure inconnue)

(Couru sur struct/classe avec 1 champ, pas 10)

Avec la version build sur mon MacBook Pro:

  • La version class a pris 1.10082 sec
  • La version struct a pris 0.02324 sec (50 fois plus rapide)
148
Khanh Nguyen

Similarités entre structs et classes.

J'ai créé Gist pour cela avec des exemples simples . https://github.com/objc-Swift/swift-classes-vs-structures

Et les différences

1. Héritage.

les structures ne peuvent pas hériter dans Swift. Si tu veux

class Vehicle{
}

class Car : Vehicle{
}

Aller pour un cours.

2. passer par

Les structures Swift passent par valeur et les instances de classe passent par référence.

Différences contextuelles

Constante de structure et variables

Exemple (utilisé à la WWDC 2014)

struct Point{

   var x = 0.0;
   var y = 0.0;

} 

Définit une structure appelée Point.

var point = Point(x:0.0,y:2.0)

Maintenant, si j'essaie de changer le x. C'est une expression valide.

point.x = 5

Mais si je définissais un point comme constant.

let point = Point(x:0.0,y:2.0)
point.x = 5 //This will give compile time error.

Dans ce cas, le point entier est une constante immuable. 

Si j'ai utilisé une classe Point à la place, il s'agit d'une expression valide. Parce que dans une classe, la constante immuable est la référence à la classe elle-même et non à ses variables d'instance (sauf si ces variables sont définies comme des constantes)

59
MadNik

Voici d'autres raisons à considérer:

  1. les structures obtiennent un initialiseur automatique que vous n'avez pas du tout besoin de conserver dans le code.

    struct MorphProperty {
       var type : MorphPropertyValueType
       var key : String
       var value : AnyObject
    
       enum MorphPropertyValueType {
           case String, Int, Double
       }
     }
    
     var m = MorphProperty(type: .Int, key: "what", value: "blah")
    

Pour obtenir cela dans une classe, vous devez ajouter l'initialiseur et maintenir l'intializer ... 

  1. Les types de collection de base tels que Array sont des structures. Plus vous les utiliserez dans votre propre code, plus vous vous habituerez à passer par valeur plutôt que par référence. Par exemple:

    func removeLast(var array:[String]) {
       array.removeLast()
       println(array) // [one, two]
    }
    
    var someArray = ["one", "two", "three"]
    removeLast(someArray)
    println(someArray) // [one, two, three]
    
  2. Apparemment, l’immuabilité contre la mutabilité est un sujet énorme, mais beaucoup de gens intelligents pensent que l’immuabilité - dans ce cas, les structures - est préférable. Objets mutables vs immuables

28
Dan Rosenstark

En supposant que nous sachions que Struct est un type valeur et Class est un type référence.

Si vous ne savez pas ce que sont un type de valeur et un type de référence, voir Quelle est la différence entre le passage par référence et le passage par valeur?

Basé sur le post de mikeash :

... Voyons d'abord quelques exemples évidents et évidents. Les entiers sont évidemment copiable. Ils devraient être des types de valeur. Les prises réseau ne peuvent pas être sensiblement copié. Ils devraient être des types de référence. Points, comme dans x, y paires, sont copiables. Ils devraient être des types de valeur. Un contrôleur qui représente un disque ne peut pas être sensiblement copié. Cela devrait être une référence type.

Certains types peuvent être copiés, mais ce n'est peut-être pas quelque chose que vous souhaitez arriver tout le temps. Cela suggère qu'ils devraient être une référence les types. Par exemple, un bouton à l'écran peut être copié de manière conceptuelle . La copie ne sera pas tout à fait identique à l'original. Un clic sur le La copie n'active pas l'original. La copie n'occupera pas le même emplacement sur l'écran. Si vous passez le bouton ou le mettez dans un fichier nouvelle variable, vous voudrez probablement faire référence au bouton d'origine, et vous ne voudriez faire une copie que si elle est explicitement demandée. Cette signifie que votre type de bouton doit être un type de référence.

Les contrôleurs de vue et de fenêtre sont un exemple similaire. Ils pourraient être copiable, peut-être, mais ce n’est presque jamais ce que vous voudriez faire . Ils devraient être des types de référence.

Qu'en est-il des types de modèles? Vous pourriez avoir un type d'utilisateur représentant un utilisateur sur votre système, ou un type de crime représentant une action entreprise par un Utilisateur. Celles-ci sont joliment copiables, elles devraient donc probablement avoir une valeur. les types. Cependant, vous voulez probablement que les mises à jour du crime d'un utilisateur soient effectuées dans une place dans votre programme pour être visible aux autres parties du programme . Cela suggère que vos utilisateurs devraient être gérés par une sorte d’utilisateur contrôleur qui serait un type de référence. par exemple

struct User {}
class UserController {
    var users: [User]

    func add(user: User) { ... }
    func remove(userNamed: String) { ... }
    func ...
}

Les collections sont un cas intéressant. Ceux-ci incluent des choses comme des tableaux et des dictionnaires, ainsi que des chaînes. Sont-ils copiables? Évidemment. Est copier quelque chose que vous voulez arriver facilement et souvent? C'est moins clair.

La plupart des langues disent "non" à cela et font référence à leurs collections les types. Cela est vrai dans Objective-C, Java, Python et JavaScript et presque toutes les autres langues auxquelles je peux penser. (Une exception majeure Est le C++ avec des types de collection STL, mais C++ est le fou délirant du monde du langage Qui fait tout étrangement.)

Swift a répondu "oui", ce qui signifie que des types tels que Array, Dictionary et Les chaînes sont des structures plutôt que des classes. Ils sont copiés lors de la cession, et en les passant comme paramètres. C’est un choix tout à fait judicieux tant que la copie est bon marché, ce que Swift s'efforce de faire accomplir. ...

De plus, n'utilisez pas la classe lorsque vous devez redéfinir chaque instance d'une fonction, c'est-à-dire qu'elle ne possède aucune fonctionnalité partagée.

Donc, au lieu d'avoir plusieurs sous-classes d'une classe. Utilisez plusieurs structures conformes à un protocole.

18
Honey

Quelques avantages:

  • automatiquement threadsafe car non partageable
  • utilise moins de mémoire en raison de no isa et de refcount (et en fait, la pile est généralement allouée)
  • les méthodes sont toujours distribuées statiquement, donc peuvent être en ligne (bien que @final puisse le faire pour les classes)
  • plus facile à raisonner (pas besoin de "copier de manière défensive" comme c'est typique avec NSArray, NSString, etc ...) pour les mêmes raisons que la sécurité des threads
18
Catfish_Man

La structure est beaucoup plus rapide que la classe. De plus, si vous avez besoin d'héritage, vous devez utiliser Class. Le point le plus important est que Class est le type de référence alors que Structure est le type de valeur. par exemple,

class Flight {
    var id:Int?
    var description:String?
    var destination:String?
    var airlines:String?
    init(){
        id = 100
        description = "first ever flight of Virgin Airlines"
        destination = "london"
        airlines = "Virgin Airlines"
    } 
}

struct Flight2 {
    var id:Int
    var description:String
    var destination:String
    var airlines:String  
}

permet maintenant de créer une instance des deux.

var flightA = Flight()

var flightB = Flight2.init(id: 100, description:"first ever flight of Virgin Airlines", destination:"london" , airlines:"Virgin Airlines" )

passons maintenant ces instances à deux fonctions qui modifient l’id, la description, la destination, etc.

func modifyFlight(flight:Flight) -> Void {
    flight.id = 200
    flight.description = "second flight of Virgin Airlines"
    flight.destination = "new york"
    flight.airlines = "Virgin Airlines"
}

également,

func modifyFlight2(flight2: Flight2) -> Void {
    var passedFlight = flight2
    passedFlight.id = 200
    passedFlight.description = "second flight from virgin airlines" 
}

alors,

modifyFlight(flight: flightA)
modifyFlight2(flight2: flightB)

maintenant, si nous imprimons l'identifiant et la description du flightA, nous obtenons

id = 200
description = "second flight of Virgin Airlines"

Nous pouvons voir ici que l'id et la description de FlightA sont modifiés car le paramètre transmis à la méthode modify pointe en fait sur l'adresse mémoire de l'objet flightA (type de référence).

maintenant, si nous imprimons l'id et la description de l'instance FLightB, nous obtenons, 

id = 100
description = "first ever flight of Virgin Airlines"

Nous pouvons voir ici que l'instance FlightB n'est pas modifiée car dans la méthode modifyFlight2, l'instance réelle de Flight2 est transmise plutôt que référence (type de valeur).

12
Manoj Karki

Répondre à la question du point de vue des types de valeur par rapport aux types de référence, de cet article de blog d'Apple il semblerait très simple:

Utilisez un type de valeur [par ex. struct, enum] quand:

  • Comparer les données d'instance avec == est logique
  • Vous voulez que les copies aient un état indépendant
  • Les données seront utilisées dans le code sur plusieurs threads

Utilisez un type de référence [par exemple. classe] quand:

  • Comparer l'identité de l'instance avec === est logique
  • Vous voulez créer un état mutable et partagé

Comme mentionné dans cet article, une classe sans propriétés enregistrables se comportera de manière identique à une structure, avec (j'ajouterai) une mise en garde: les structures sont les meilleures pour les modèles thread-safe - une exigence de plus en plus imminente dans l'architecture d'applications moderne .

4
David James

Avec les classes, vous obtenez l'héritage et êtes passé par référence, les structures n'ont pas d'héritage et sont passées par valeur.

Il existe d'excellentes sessions WWDC sur Swift, l'une des réponses à cette question précise est détaillée. Assurez-vous de les regarder car cela vous permettra de vous mettre au diapason beaucoup plus rapidement que le guide des langues ou l'iBook.

3
Joride

Je ne dirais pas que les structures offrent moins de fonctionnalités.

Bien sûr, le moi est immuable sauf dans une fonction de mutation, mais c'est à peu près tout.

L'héritage fonctionne bien tant que vous vous en tenez à la bonne vieille idée selon laquelle chaque classe devrait être abstraite ou finale.

Implémentez des classes abstraites en tant que protocoles et des classes finales en tant que structures.

La bonne chose à propos de structs est que vous pouvez rendre vos champs mutables sans créer d’état mutable partagé car la copie sur écriture s’occupe de ça :)

C'est pourquoi les propriétés/champs de l'exemple suivant sont tous mutables, ce que je ne ferais pas en Java, C # ou Swift classes .

Exemple de structure d'héritage avec un peu d'utilisation sale et directe au bas de la fonction nommée "exemple":

protocol EventVisitor
{
    func visit(event: TimeEvent)
    func visit(event: StatusEvent)
}

protocol Event
{
    var ts: Int64 { get set }

    func accept(visitor: EventVisitor)
}

struct TimeEvent : Event
{
    var ts: Int64
    var time: Int64

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }
}

protocol StatusEventVisitor
{
    func visit(event: StatusLostStatusEvent)
    func visit(event: StatusChangedStatusEvent)
}

protocol StatusEvent : Event
{
    var deviceId: Int64 { get set }

    func accept(visitor: StatusEventVisitor)
}

struct StatusLostStatusEvent : StatusEvent
{
    var ts: Int64
    var deviceId: Int64
    var reason: String

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }

    func accept(visitor: StatusEventVisitor)
    {
        visitor.visit(self)
    }
}

struct StatusChangedStatusEvent : StatusEvent
{
    var ts: Int64
    var deviceId: Int64
    var newStatus: UInt32
    var oldStatus: UInt32

    func accept(visitor: EventVisitor)
    {
        visitor.visit(self)
    }

    func accept(visitor: StatusEventVisitor)
    {
        visitor.visit(self)
    }
}

func readEvent(fd: Int) -> Event
{
    return TimeEvent(ts: 123, time: 56789)
}

func example()
{
    class Visitor : EventVisitor
    {
        var status: UInt32 = 3;

        func visit(event: TimeEvent)
        {
            print("A time event: \(event)")
        }

        func visit(event: StatusEvent)
        {
            print("A status event: \(event)")

            if let change = event as? StatusChangedStatusEvent
            {
                status = change.newStatus
            }
        }
    }

    let visitor = Visitor()

    readEvent(1).accept(visitor)

    print("status: \(visitor.status)")
}
2
yeoman

Dans Swift, un nouveau modèle de programmation appelé Programmation orientée protocole a été introduit.

Motif de création:

Dans Swift, Struct est un type valeur qui est automatiquement cloné. Par conséquent, nous obtenons le comportement requis pour mettre en œuvre le modèle de prototype gratuitement. 

Alors que classes est le type de référence, qui n'est pas automatiquement cloné lors de l'affectation. Pour implémenter le modèle prototype, les classes doivent adopter le protocole NSCopying.


Shallow copy copie uniquement la référence, qui pointe sur ces objets, tandis que deep copy duplique la référence de l’objet.


Implémenter deep copy pour chaque type de référence est devenu une tâche fastidieuse. Si les classes incluent un type de référence supplémentaire, nous devons implémenter un modèle prototype pour chacune des propriétés de référence. Et nous devons ensuite copier le graphe d'objet entier en implémentant le protocole NSCopying.

class Contact{
  var firstName:String
  var lastName:String
  var workAddress:Address // Reference type
}

class Address{
   var street:String
   ...
} 

En utilisant structs et enums, nous avons simplifié notre code car nous n’avions pas à implémenter la logique de copie.

2
Balasubramanian

Structs are value type et Classes sont reference type

  • Les types de valeur sont plus rapides que les types de référence
  • Les instances de type valeur sont sécurisées dans un environnement multi-thread, étant donné que plusieurs threads peuvent muter l'instance sans avoir à s'inquiéter des conditions de concurrence ou des blocages
  • Le type de valeur n'a pas de références contrairement au type de référence; il n'y a donc aucune fuite de mémoire.

Utilisez un type value lorsque:

  • Vous voulez que les copies aient un état indépendant, les données seront utilisées dans le code Sur plusieurs threads

Utilisez un type reference lorsque:

  • Vous voulez créer un état partagé, modifiable.

Vous trouverez également des informations supplémentaires dans la documentation Apple.

https://docs.Swift.org/Swift-book/LanguageGuide/ClassesAndStructures.html


Information additionnelle

Les types de valeur Swift sont conservés dans la pile. Dans un processus, chaque thread a son propre espace de pile, de sorte qu'aucun autre thread ne pourra accéder directement à votre type de valeur. Par conséquent, pas de conditions de concurrence, de verrous, d'interblocages ou de complexité de synchronisation de threads connexe.

Les types de valeur n'ont pas besoin d'allocation dynamique de mémoire ni de comptage de références, deux opérations coûteuses. En même temps, les méthodes sur les types de valeur sont distribuées de manière statique. Celles-ci créent un énorme avantage en faveur des types de valeur en termes de performances.

Pour rappel, voici une liste de Swift 

Types de valeur:

  • Struct
  • Enum
  • Tuple
  • Primitives (Int, Double, Bool etc.)
  • Collections (tableau, chaîne, dictionnaire, ensemble)

Types de référence:

  • Classe
  • Tout ce qui vient de NSObject
  • Une fonction
  • Fermeture
1
casillas

De nombreuses API Cocoa nécessitent des sous-classes NSObject, ce qui vous oblige à utiliser class. Mais à part cela, vous pouvez utiliser les cas suivants du blog Swift d’Apple pour décider d’utiliser un type de valeur struct/enum ou un type de référence de classe.

https://developer.Apple.com/Swift/blog/?id=10

1
akshay

Comme struct sont des types de valeur et que vous pouvez très facilement créer la mémoire qui est stockée dans la pile.Struct est facilement accessible et une fois l'étendue du travail effectuée, il est facilement désaffecté de la mémoire de la pile en sautant du haut de la pile. d'autre part, la classe est un type de référence stocké dans le tas et les modifications apportées à un objet de la classe auront un impact sur les autres objets, car ils sont étroitement couplés au type de référence. Tous les membres d'une structure sont publics tandis que tous les membres d'une classe sont privés.

L'inconvénient de struct est qu'il ne peut pas être hérité.

0
Tapash Mollick

Un point qui ne retient pas l'attention dans ces réponses est qu'une variable détenant une classe par rapport à une structure peut être une let tout en permettant des modifications sur les propriétés de l'objet, alors que vous ne pouvez pas le faire avec une structure.

Ceci est utile si vous ne voulez pas que la variable pointe jamais vers un autre objet, mais que vous deviez quand même modifier l'objet, c'est-à-dire si vous souhaitez mettre à jour plusieurs variables d'instance les unes après les autres. Si c'est une structure, vous devez autoriser la réinitialisation de la variable sur un autre objet en utilisant var afin de le faire, puisqu'un type de valeur constante dans Swift autorise correctement la mutation zéro, alors que les types de référence (classes) ne se comportent pas de cette façon. .

0
johnbakers