Variable d'instance sur une classe:
class Parent
@things = []
def self.things
@things
end
def things
self.class.things
end
end
class Child < Parent
@things = []
end
Parent.things << :car
Child.things << :doll
mom = Parent.new
dad = Parent.new
p Parent.things #=> [:car]
p Child.things #=> [:doll]
p mom.things #=> [:car]
p dad.things #=> [:car]
Variable de classe:
class Parent
@@things = []
def self.things
@@things
end
def things
@@things
end
end
class Child < Parent
end
Parent.things << :car
Child.things << :doll
p Parent.things #=> [:car,:doll]
p Child.things #=> [:car,:doll]
mom = Parent.new
dad = Parent.new
son1 = Child.new
son2 = Child.new
daughter = Child.new
[ mom, dad, son1, son2, daughter ].each{ |person| p person.things }
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
#=> [:car, :doll]
Avec une variable d'instance sur une classe (pas sur une instance de cette classe), vous pouvez stocker quelque chose de commun à cette classe sans que les sous-classes les obtiennent automatiquement (et vice-versa). Avec les variables de classe, vous avez la commodité de ne pas avoir à écrire à self.class
partir d'un objet d'instance et (lorsque cela est souhaitable) vous obtenez également un partage automatique dans toute la hiérarchie de classes.
Les fusionner en un seul exemple qui couvre également les variables d'instance sur les instances:
class Parent
@@family_things = [] # Shared between class and subclasses
@shared_things = [] # Specific to this class
def self.family_things
@@family_things
end
def self.shared_things
@shared_things
end
attr_accessor :my_things
def initialize
@my_things = [] # Just for me
end
def family_things
self.class.family_things
end
def shared_things
self.class.shared_things
end
end
class Child < Parent
@shared_things = []
end
Et puis en action:
mama = Parent.new
papa = Parent.new
joey = Child.new
suzy = Child.new
Parent.family_things << :house
papa.family_things << :vacuum
mama.shared_things << :car
papa.shared_things << :blender
papa.my_things << :quadcopter
joey.my_things << :bike
suzy.my_things << :doll
joey.shared_things << :puzzle
suzy.shared_things << :blocks
p Parent.family_things #=> [:house, :vacuum]
p Child.family_things #=> [:house, :vacuum]
p papa.family_things #=> [:house, :vacuum]
p mama.family_things #=> [:house, :vacuum]
p joey.family_things #=> [:house, :vacuum]
p suzy.family_things #=> [:house, :vacuum]
p Parent.shared_things #=> [:car, :blender]
p papa.shared_things #=> [:car, :blender]
p mama.shared_things #=> [:car, :blender]
p Child.shared_things #=> [:puzzle, :blocks]
p joey.shared_things #=> [:puzzle, :blocks]
p suzy.shared_things #=> [:puzzle, :blocks]
p papa.my_things #=> [:quadcopter]
p mama.my_things #=> []
p joey.my_things #=> [:bike]
p suzy.my_things #=> [:doll]
self.things
référencé une méthodethings
dans la portée actuelle (dans le cas d'une instance d'une classe, ce sera la méthode de l'instance), oùself.class.things
référence unethings
méthode de la classe de la portée actuelle (encore une fois dans le cas d'une instance d'une classe, cela signifierait la méthode de classe).Je crois que la principale (seule?) Différence est l'héritage:
Les variables de classe sont partagées par toutes les "instances de classe" (c'est-à-dire les sous-classes), alors que les variables d'instance de classe sont spécifiques à cette classe uniquement. Mais si vous n'avez jamais l'intention d'étendre votre cours, la différence est purement académique.
la source
S.new.s => nil
etS.new.k => 23
.La source
Disponibilité aux méthodes d'instance
Héritabilité
la source
Comme d'autres l'ont dit, les variables de classe sont partagées entre une classe donnée et ses sous-classes. Les variables d'instance de classe appartiennent à exactement une classe; ses sous-classes sont séparées.
Pourquoi ce comportement existe-t-il? Eh bien, tout dans Ruby est un objet, même des classes. Cela signifie que chaque classe a un objet de la classe
Class
(ou plutôt, une sous-classe deClass
) qui lui correspond. (Quand vous ditesclass Foo
, vous déclarez vraiment une constanteFoo
et lui assignez un objet de classe.) Et chaque objet Ruby peut avoir des variables d'instance, donc les objets de classe peuvent aussi avoir des variables d'instance.Le problème est que les variables d'instance sur les objets de classe ne se comportent pas vraiment comme vous le souhaitez habituellement. Vous voulez généralement qu'une variable de classe définie dans une superclasse soit partagée avec ses sous-classes, mais ce n'est pas ainsi que les variables d'instance fonctionnent - la sous-classe a son propre objet de classe et cet objet de classe a ses propres variables d'instance. Ils ont donc introduit des variables de classe distinctes avec le comportement que vous souhaitiez probablement.
En d'autres termes, les variables d'instance de classe sont en quelque sorte un accident de la conception de Ruby. Vous ne devriez probablement pas les utiliser à moins de savoir spécifiquement qu'ils sont ce que vous recherchez.
la source
FAQ officielle sur Ruby: Quelle est la différence entre les variables de classe et les variables d'instance de classe?
La principale différence est le comportement concernant l'héritage: les variables de classe sont partagées entre une classe et toutes ses sous-classes, tandis que les variables d'instance de classe n'appartiennent qu'à une classe spécifique.
Les variables de classe peuvent d'une certaine manière être considérées comme des variables globales dans le contexte d'une hiérarchie d'héritage, avec tous les problèmes qui viennent avec les variables globales. Par exemple, une variable de classe pourrait (accidentellement) être réaffectée par l'une de ses sous-classes, affectant toutes les autres classes:
Ou, une classe d'ancêtre pourrait être rouverte et modifiée plus tard, avec des effets éventuellement surprenants:
Donc, à moins que vous ne sachiez exactement ce que vous faites et que vous ayez explicitement besoin de ce type de comportement, il vaut mieux utiliser des variables d'instance de classe.
la source
Pour ceux qui ont une formation C ++, vous pouvez être intéressé par une comparaison avec l'équivalent C ++:
Comme nous pouvons le voir,
k
est unestatic
variable similaire. C'est 100% comme une variable globale, sauf qu'elle appartient à la classe ( portée pour être correcte). Cela permet d'éviter plus facilement les conflits entre des variables de nom similaire. Comme toute variable globale, il n'y a qu'une seule instance de cette variable et sa modification est toujours visible par tous.D'autre part,
s
est une valeur spécifique à un objet. Chaque objet a sa propre instance de la valeur. En C ++, vous devez créer une instance pour avoir accès à cette variable. Dans Ruby, la définition de classe est elle-même une instance de la classe (en JavaScript, cela s'appelle un prototype), vous pouvez donc y accéders
depuis la classe sans instanciation supplémentaire. L'instance de classe peut être modifiée, mais la modification des
va être spécifique à chaque instance (chaque objet de typeS
). Ainsi, en modifier un ne changera pas la valeur d'un autre.la source
Bien qu'il puisse sembler immédiatement utile d'utiliser des variables d'instance de classe, étant donné que les variables d'instance de classe sont partagées entre les sous-classes et qu'elles peuvent être référencées à la fois dans les méthodes singleton et d'instance, il y a un inconvénient majeur. Ils sont partagés et les sous-classes peuvent donc changer la valeur de la variable d'instance de classe, et la classe de base sera également affectée par le changement, ce qui est généralement un comportement indésirable:
Rails introduit une méthode pratique appelée class_attribute. Comme son nom l'indique, il déclare un attribut de niveau classe dont la valeur peut être héritée par les sous-classes. La valeur class_attribute est accessible à la fois dans les méthodes singleton et d'instance, comme c'est le cas avec la variable d'instance de classe. Cependant, l'énorme avantage de class_attribute dans Rails est que les sous-classes peuvent changer leur propre valeur et cela n'aura aucun impact sur la classe parente.
la source
self.
chaque fois que vous souhaitez accéder à l'attributc
, par exempleself.c
. La documentation indique qu'undefault:
paramètre peut être transmisclass_attribute
mais cela ne semble pas fonctionner en raison du point que je viens de mentionnerself
.