174 votes

Affectation dynamique des constantes

class MyClass
  def mymethod
    MYCONSTANT = "blah"
  end
end

me donne l'erreur :

SyntaxError : erreur d'affectation d'une constante dynamique

Pourquoi cela est-il considéré comme une constante dynamique ? Je lui attribue simplement une chaîne de caractères.

44 votes

La constante dynamique est quelque chose comme l'eau sèche :)

52 votes

Il n'est pas dit que la constante est dynamique. Il est dit que l'affectation est dynamique.

174voto

Phrogz Points 112337

Votre problème est qu'à 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 la méthode contenu de la chaîne sont les mêmes (pour le moment, en tout cas), la chaîne réelle objet est 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éreriez-vous avoir une variable d'instance sur 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 voulez changer la valeur d'une constante dans une méthode, et que votre constante est une chaîne de caractères ou un tableau, vous pouvez "tricher" et utiliser l'attribut #replace pour que l'objet prenne une nouvelle valeur sans que l'objet soit réellement modifié :

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"

22 votes

Le PO n'a jamais dit qu'il voulait changement la valeur de la constante, mais je voulais juste lui attribuer une valeur. Le cas d'utilisation fréquent menant à cette erreur Ruby est celui où vous construisez la valeur d'une méthode à partir d'autres actifs d'exécution (variables, arguments de ligne de commande, ENV), typiquement dans un constructeur, par ex. def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/‌​#{db}") end . C'est l'un de ces cas où Ruby n'a pas de méthode simple.

2 votes

@ArnaudMeuret Dans ce cas, vous voulez une variable d'instance (ex. @variable ), et non une constante. Sinon, vous réassigneriez DB à chaque fois que vous instanciez une nouvelle instance de cette classe.

3 votes

@Ajedi32 Cette situation résulte généralement de contraintes externes et non de choix de conception, comme dans mon exemple avec Sequel. Ce que je veux dire, c'est que l'attribution d'une valeur à une constante est autorisée par Ruby dans certains domaines et pas dans d'autres. Auparavant, c'était au développeur de choisir judicieusement quand effectuer l'affectation. Ruby a changé sur ce point. Pas pour le bien de tous.

85voto

Ajedi32 Points 5367

Comme les constantes en Ruby ne sont pas destinées à être modifiées, Ruby vous décourage de les assigner dans des parties du code qui pourraient être exécutées plus d'une fois, comme à l'intérieur des méthodes.

Dans des circonstances normales, vous devriez définir la constante à l'intérieur de 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 à l'intérieur d'une méthode (peut-être pour un certain type de métaprogrammation), vous pouvez utiliser la procédure suivante 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"

Mais encore une fois, const_set n'est pas quelque chose que vous devriez vraiment avoir à faire dans des circonstances normales. Si vous n'êtes pas sûr de savoir si vous vraiment Si vous voulez assigner des constantes de cette manière, vous pouvez envisager l'une des alternatives suivantes :

Variables de classe

Les variables de classe se comportent comme des constantes à bien des égards. Ce sont des propriétés d'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 assignées à l'intérieur des méthodes 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". Ils se comportent un peu comme les 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 être complet, je devrais probablement mentionner que si vous avez besoin d'assigner une valeur qui ne peut être déterminée qu'après l'instanciation de votre classe, il y a de fortes chances que vous cherchiez en fait une bonne vieille 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

46voto

David Grayson Points 22459

En Ruby, toute variable dont le nom commence par une majuscule est une constante et vous ne pouvez lui attribuer 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

6 votes

Dieu merci, quelqu'un a mentionné que "toute variable dont le nom commence par une majuscule est une constante !".

17voto

chrispanda Points 2368

Les constantes en ruby ne peuvent pas être définies à l'intérieur des méthodes. Voir les notes au bas de cette page, par exemple

3voto

Jose Paez Points 33

Vous ne pouvez pas nommer une variable avec des majuscules ou Ruby supposera qu'il s'agit d'une constante et voudra que sa valeur reste constante, auquel cas le changement de sa valeur serait une erreur, une "erreur d'affectation de constante dynamique". Avec des minuscules, ça devrait aller

class MyClass
  def mymethod
    myconstant = "blah"
  end
end

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X