class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
me donne l'erreur:
SyntaxError: erreur d'affectation de constante dynamique
Pourquoi est-ce considéré comme une constante dynamique? Je lui attribue simplement une chaîne.
Votre problème est que chaque fois que vous exécutez la méthode, vous attribuez une nouvelle valeur à la constante. Ceci n'est pas autorisé car cela rend la constante non constante; Même si les contenus de la chaîne sont identiques (pour le moment, quoi qu'il en soit), la chaîne réelle object est elle-même différente chaque fois que la méthode est appelée. Par exemple:
def foo
p "bar".object_id
end
foo #=> 15779172
foo #=> 15779112
Peut-être que si vous expliquiez votre cas d'utilisation (pourquoi vous voulez changer la valeur d'une constante dans une méthode), nous pourrions vous aider avec une meilleure implémentation.
Peut-être préférez-vous une variable d'instance dans la classe?
class MyClass
class << self
attr_accessor :my_constant
end
def my_method
self.class.my_constant = "blah"
end
end
p MyClass.my_constant #=> nil
MyClass.new.my_method
p MyClass.my_constant #=> "blah"
Si vous vraiment souhaitez modifier la valeur d'une constante dans une méthode et que votre constante est une chaîne ou un tableau, vous pouvez "tricher" et utiliser la méthode #replace
pour que l'objet prenne une nouvelle valeur changer réellement l'objet:
class MyClass
BAR = "blah"
def cheat(new_bar)
BAR.replace new_bar
end
end
p MyClass::BAR #=> "blah"
MyClass.new.cheat "whee"
p MyClass::BAR #=> "whee"
Comme les constantes dans Ruby ne doivent pas être modifiées, Ruby vous déconseille de les affecter dans des parties de code pouvant être exécutées plusieurs fois, telles que des méthodes internes.
Dans des circonstances normales, vous devez définir la constante dans la classe elle-même:
class MyClass
MY_CONSTANT = "foo"
end
MyClass::MY_CONSTANT #=> "foo"
Si pour une raison quelconque vous avez vraiment besoin de définir une constante dans une méthode (peut-être pour un certain type de métaprogrammation), vous pouvez utiliser const_set
:
class MyClass
def my_method
self.class.const_set(:MY_CONSTANT, "foo")
end
end
MyClass::MY_CONSTANT
#=> NameError: uninitialized constant MyClass::MY_CONSTANT
MyClass.new.my_method
MyClass::MY_CONSTANT #=> "foo"
Encore une fois cependant, const_set
n’est pas une chose à laquelle vous devriez vraiment avoir recours dans des circonstances normales. Si vous ne savez pas si vraiment souhaitez affecter les constantes de cette manière, vous pouvez envisager l'une des solutions suivantes:
Les variables de classe se comportent comme des constantes à bien des égards. Ce sont des propriétés sur une classe et elles sont accessibles dans les sous-classes de la classe sur laquelle elles sont définies.
La différence est que les variables de classe sont censées être modifiables et peuvent donc être affectées à des méthodes internes sans problème.
class MyClass
def self.my_class_variable
@@my_class_variable
end
def my_method
@@my_class_variable = "foo"
end
end
class SubClass < MyClass
end
MyClass.my_class_variable
#=> NameError: uninitialized class variable @@my_class_variable in MyClass
SubClass.my_class_variable
#=> NameError: uninitialized class variable @@my_class_variable in MyClass
MyClass.new.my_method
MyClass.my_class_variable #=> "foo"
SubClass.my_class_variable #=> "foo"
Les attributs de classe sont une sorte de "variable d'instance sur une classe". Elles se comportent un peu comme des variables de classe, sauf que leurs valeurs ne sont pas partagées avec les sous-classes.
class MyClass
class << self
attr_accessor :my_class_attribute
end
def my_method
self.class.my_class_attribute = "blah"
end
end
class SubClass < MyClass
end
MyClass.my_class_attribute #=> nil
SubClass.my_class_attribute #=> nil
MyClass.new.my_method
MyClass.my_class_attribute #=> "blah"
SubClass.my_class_attribute #=> nil
SubClass.new.my_method
SubClass.my_class_attribute #=> "blah"
Et pour compléter, je devrais probablement mentionner: si vous devez attribuer une valeur qui ne peut être déterminée qu'une fois votre classe instanciée, il y a de bonnes chances que vous recherchiez une ancienne variable d'instance.
class MyClass
attr_accessor :instance_variable
def my_method
@instance_variable = "blah"
end
end
my_object = MyClass.new
my_object.instance_variable #=> nil
my_object.my_method
my_object.instance_variable #=> "blah"
MyClass.new.instance_variable #=> nil
En Ruby, toute variable dont le nom commence par une majuscule est une constante et vous ne pouvez l'affecter qu'une seule fois. Choisissez l'une de ces alternatives:
class MyClass
MYCONSTANT = "blah"
def mymethod
MYCONSTANT
end
end
class MyClass
def mymethod
my_constant = "blah"
end
end
Les constantes dans Ruby ne peuvent pas être définies dans les méthodes. Voir les notes au bas de cette page, par exemple
Ruby n'aime pas que vous affectiez la constante à l'intérieur d'une méthode car elle risquerait d'être réaffectée. Plusieurs SO réponses avant moi proposent l'alternative de l'affecter à une méthode - mais dans la classe, qui est un meilleur endroit pour l'assigner.
Un grand merci à Dorian et à Phrogz de m'avoir rappelé la méthode #replace de array (et de hachage), qui peut "remplacer le contenu d'un tableau ou d'un hachage".
La notion selon laquelle la valeur d'une constante peut être modifiée, mais avec un avertissement agaçant, est l'une des rares erreurs de conception de Ruby - celles-ci doivent être soit totalement immuables, soit vider complètement l'idée de base. Du point de vue du codeur, une constante est déclarative et intentionnelle, ce qui indique à l'autre que "cette valeur est vraiment non modifiable une fois déclarée/attribuée."
Mais parfois, une "déclaration évidente" exclut en réalité d'autres opportunités utiles pour l'avenir. Par exemple...
Il y a sont cas d'utilisation légitimes dans lesquels une valeur de "constante" doit réellement être modifiée: par exemple, recharger ARGV à partir d'une boucle d'invite de type REPL, puis réexécuter ARGV via davantage d'OptionParser ultérieur. analyser! appelle - le tour est joué! Donne "argument de ligne de commande" un nouvel utilitaire dynamique.
Le problème pratique est soit avec l'hypothèse présumée selon laquelle "ARGV doit être une constante", ou dans la méthode d'initialisation propre à optparse, qui code en dur l'affectation d'ARGV à l'instance var @default_argv pour un traitement ultérieur. - ce tableau (ARGV) devrait vraiment être un paramètre, encourageant la ré-analyse et la réutilisation, le cas échéant. Un paramétrage approprié, avec une valeur par défaut appropriée (par exemple, ARGV), éviterait de devoir changer l’ARGV "constant". Juste quelques 2 ¢ dignes de pensées ...
Vous ne pouvez pas nommer une variable en lettres majuscules, sinon Ruby supposera que c'est une constante et voudra que sa valeur reste constante. Dans ce cas, changer sa valeur serait une erreur ou une "erreur d'affectation de constante dynamique". Avec minuscule devrait être bien
class MyClass
def mymethod
myconstant = "blah"
end
end