web-dev-qa-db-fra.com

Affectation dynamique constante

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.

123
themirror

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"
122
Phrogz

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:

Variables de classe

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"

Attributs de classe

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"

Variables d'instance

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
64
Ajedi32

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
23
David Grayson

Les constantes dans Ruby ne peuvent pas être définies dans les méthodes. Voir les notes au bas de cette page, par exemple

14
chrispanda

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.

0
John

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 ...

0
Lorin Ricker

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
0
Jose Paez