J'avais oublié qu'il y était un concept de "variable d'instance de classe" dans Ruby. En tout cas, le problème de l'OP semblait déroutant, et n'était pas vraiment abordé dans aucune des réponses jusqu'à présent, sauf pour un indice dans la réponse de kch: c'est un problème de portée. (Ajouté sur edit: En fait, la réponse de sris adresse ce point à la fin, mais je laisserai cette réponse de toute façon, car je pense que l'exemple de code pourrait être utile pour comprendre le problème.)
Dans un classe Ruby, un nom de variable commençant par @
peut se référer à l'un des deux variables soit à une variable instance ou à une instance de classe variables, selon l'endroit où dans la classe il est appelé. C'est un gotcha assez subtile.
Un exemple clarifiera le point. Voici une petite classe de test Ruby (tous code testé RIR):
class T
@@class_variable = "BBQ"
@class_instance_variable_1 = "WTF"
@class_instance_variable_2 = "LOL"
def self.class_method
puts "@@class_variable == #{@@class_variable || 'nil'}"
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
def initialize
@instance_variable = "omg"
# The following line does not assign a value to the class instance variable,
# but actually declares an instance variable withthe same name!
@class_instance_variable_1 = "wtf"
puts "@@class_variable == #{@@class_variable || 'nil'}"
# The following two lines do not refer to the class instance variables,
# but to the instance variables with the same names.
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
def instance_method
puts "@@class_variable == #{@@class_variable || 'nil'}"
# The following two lines do not refer to the class instance variables,
# but to the instance variables with the same names.
puts "@class_instance_variable_1 == #{@class_instance_variable_1 || 'nil'}"
puts "@class_instance_variable_2 == #{@class_instance_variable_2 || 'nil'}"
puts "@instance_variable == #{@instance_variable || 'nil'}"
end
end
que je peux nommer les variables d'après ce que je pensais qu'ils étaient, mais qui se révèle ne pas être toujours le cas:
irb> T.class_method
@@class_variable == BBQ
@class_instance_variable_1 == WTF # the value of the class instance variable
@class_instance_variable_2 == LOL # the value of the class instance variable
@instance_variable == nil # does not exist in the class scope
=> nil
irb> t = T.new
@@class_variable == BBQ
@class_instance_variable_1 == wtf # the value of the instance variable
@class_instance_variable_2 == nil # the value of the instance variable
@instance_variable == omg
=> #<T:0x000000015059f0 @instance_variable="omg", @class_instance_variable_1="wtf">
irb> t.instance_method
@@class_variable == BBQ
@class_instance_variable_1 == wtf # the value of the instance variable
@class_instance_variable_2 == nil # the value of the instance variable
@instance_variable == omg
=> nil
irb> T.class_method
@@class_variable == BBQ
@class_instance_variable_1 == WTF # the value of the class instance variable
@class_instance_variable_2 == LOL # the value of the class instance variable
@instance_variable == nil # does not exist in the class scope
=> nil
Les @@class_variable
et @instance_variable
se comportent toujours comme vous vous y attendez: le premier est défini au niveau de la classe et, qu'il soit référencé dans une méthode de classe ou dans une méthode d'instance, il contient la valeur qui lui est assignée en haut. Ce dernier n'a de valeur que dans un objet de la classe T
, donc dans une méthode de classe, il fait référence à une variable inconnue dont la valeur est nil
.
La méthode de classe imaginative nommée class_method
génère les valeurs de @@class_variable
et les deux @class_instance_variable
s comme prévu, c'est-à-dire, comme initialisé en haut de la classe. Cependant, dans les méthodes d'instance initialize
et instance_method
, différentes variablesdu même nom sont accessibles, c'est variables d'instance, et non pas des variables d'instance de classe.
Vous pouvez voir que l'affectation dans la méthode initialize
n'a pas affecté l'instance de classe variable de @class_instance_variable_1
, parce que l'appel ultérieur de class_method
émet son ancienne valeur, "WTF"
. Au lieu de cela, la méthode initialize
a déclaré une nouvelle variable d'instance, celle qui est également nommée (à tort) @class_instance_variable_1
. La valeur qui lui est affectée, "wtf"
, est sortie par les méthodes initialize
et instance_method
.
La @class_instance_variable_2
variable dans le code exemple est équivalent à la variable @hello
dans le problème d'origine: il est déclaré et initialisé comme une variable d'instance de classe, mais quand une méthode d'instance fait référence à une variable de ce nom, il voit effectivement une instance variable du même nom - une qui n'a jamais été déclarée, sa valeur est donc nulle.
Vous dites "ils entrent dans la vie la première fois qu'ils sont assignés" mais l'OP a montré un exemple avec affectation (apparente) plus tôt que dans votre exemple, et le problème rencontré est que cette variable n'est pas née dans la vie. – kaleidic
@kaleidic Exactement. Je suis un peu perplexe face au nombre de votes positifs sur cette réponse qui ne répond pas à la question du PO. En fait, la variable d'instance de classe '@ hello' * naît dans la ligne 2 du code d'exemple, mais le problème est que ce n'est pas la variable à laquelle la ligne 4 fait référence. Voir ma réponse ci-dessous pour plus de détails. –
Désolé, vous répondez réellement à la question à la fin, ce que j'ai réussi à manquer en première lecture. –