class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
mi dà l'errore:
SyntaxError: errore di assegnazione costante dinamica
Perché questa è considerata una costante dinamica? Sto solo assegnando una stringa ad esso.
class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
mi dà l'errore:
SyntaxError: errore di assegnazione costante dinamica
Perché questa è considerata una costante dinamica? Sto solo assegnando una stringa ad esso.
Risposte:
Il problema è che ogni volta che si esegue il metodo si assegna un nuovo valore alla costante. Questo non è permesso, poiché rende la costante non costante; anche se il contenuto della stringa è lo stesso (per il momento, comunque), l' oggetto stringa reale stesso è diverso ogni volta che viene chiamato il metodo. Per esempio:
def foo
p "bar".object_id
end
foo #=> 15779172
foo #=> 15779112
Forse se spiegassi il tuo caso d'uso, perché vuoi cambiare il valore di una costante in un metodo, potremmo aiutarti con una migliore implementazione.
Forse preferiresti avere una variabile di istanza sulla 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"
Se vuoi davvero cambiare il valore di una costante in un metodo e la tua costante è una stringa o una matrice, puoi "imbrogliare" e utilizzare il #replace
metodo per fare in modo che l'oggetto acquisisca un nuovo valore senza cambiare l'oggetto:
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"
def initialize(db,user,password) DB=Sequel.connect("postgres://#{user}:#{password}@localhost/#{db}") end
. È uno di quei casi in cui Ruby non ha un modo semplice.
@variable
), Non una costante. Altrimenti verresti riassegnare DB
ogni volta che hai istanziato una nuova istanza di quella classe.
Sequel.connect
a un costante chiamato DB . In effetti, la documentazione afferma esplicitamente che si tratta solo di una raccomandazione. Non mi sembra un vincolo esterno.
Poiché le costanti in Ruby non sono pensate per essere modificate, Ruby ti scoraggia dall'assegnarle a parti di codice che potrebbero essere eseguite più di una volta, come i metodi interni.
In circostanze normali, è necessario definire la costante all'interno della classe stessa:
class MyClass
MY_CONSTANT = "foo"
end
MyClass::MY_CONSTANT #=> "foo"
Se per qualche motivo hai davvero bisogno di definire una costante all'interno di un metodo (forse per qualche tipo di metaprogrammazione), puoi usare 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"
Ancora una volta, però, const_set
non è qualcosa a cui dovresti davvero ricorrere in circostanze normali. Se non sei sicuro di voler veramente assegnare le costanti in questo modo, potresti prendere in considerazione una delle seguenti alternative:
Le variabili di classe si comportano come costanti in molti modi. Sono proprietà di una classe e sono accessibili nelle sottoclassi della classe in cui sono definite.
La differenza è che le variabili di classe sono modificabili e possono quindi essere assegnate a metodi interni senza problemi.
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"
Gli attributi di classe sono una sorta di "variabile di istanza su una classe". Si comportano un po 'come le variabili di classe, tranne per il fatto che i loro valori non sono condivisi con le sottoclassi.
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"
E solo per completezza dovrei probabilmente menzionare: se devi assegnare un valore che può essere determinato solo dopo che la tua classe è stata istanziata, ci sono buone probabilità che tu stia effettivamente cercando una semplice vecchia variabile di istanza.
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
In Ruby, qualsiasi variabile il cui nome inizia con una lettera maiuscola è una costante e puoi assegnarla solo una volta. Scegli una di queste alternative:
class MyClass
MYCONSTANT = "blah"
def mymethod
MYCONSTANT
end
end
class MyClass
def mymethod
my_constant = "blah"
end
end
Le costanti nel rubino non possono essere definite all'interno dei metodi. Vedi le note in fondo a questa pagina, per esempio
Non puoi nominare una variabile con lettere maiuscole o Ruby ne assumerà una costante e vorrà che mantenga il suo valore costante, nel qual caso cambiarne il valore sarebbe un errore un "errore di assegnazione costante dinamica". Con lettere minuscole dovrebbe andare bene
class MyClass
def mymethod
myconstant = "blah"
end
end
A Ruby non piace che tu stia assegnando la costante all'interno di un metodo perché rischia di riassegnarla. Diverse risposte SO prima di me danno l'alternativa di assegnarlo al di fuori di un metodo, ma nella classe, che è un posto migliore per assegnarlo.
Mille grazie a Dorian e Phrogz per avermi ricordato l'array (e l'hash) metodo #replace, che può "sostituire il contenuto di un array o hash".
L'idea che il valore di una COSTANTE possa essere cambiato, ma con un fastidioso avvertimento, è uno dei pochi passi sbagliati concettuali di Ruby: questi dovrebbero essere completamente immutabili o abbandonare del tutto l'idea costante. Dal punto di vista di un programmatore, una costante è dichiarativa e intenzionale, un segnale all'altro che "questo valore è veramente immutabile una volta dichiarato / assegnato".
Ma a volte una "dichiarazione ovvia" preclude in realtà altre utili opportunità future. Per esempio...
Ci sono casi di utilizzo legittimi in cui il valore di una "costante di" potrebbe davvero bisogno di essere cambiato: per esempio, ri-caricamento ARGV dal prompt-loop REPL-like, quindi eseguire nuovamente ARGV attraverso più (successiva) OptionParser.parse! chiama - voilà! Fornisce "argent a riga di comando" un'utilità dinamica completamente nuova.
Il problema pratico è o con il presupposto presunto che "ARGV deve essere una costante", o nel metodo di inizializzazione di optparse, che codifica in modo rigido l'assegnazione di ARGV all'istanza var @default_argv per l'elaborazione successiva - quell'array (ARGV) in realtà dovrebbe essere un parametro, incoraggiando il re-analisi e il riutilizzo, se del caso. Una corretta parametrizzazione, con un'adeguata impostazione predefinita (ad esempio, ARGV) eviterebbe la necessità di cambiare mai l'ARGV "costante". Solo qualche 2 ¢ di pensieri ...