class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
gibt mir den Fehler:
SyntaxError: Zuweisungsfehler der dynamischen Konstante
Warum wird dies als dynamische Konstante angesehen? Ich weise ihm nur einen String zu.
class MyClass
def mymethod
MYCONSTANT = "blah"
end
end
gibt mir den Fehler:
SyntaxError: Zuweisungsfehler der dynamischen Konstante
Warum wird dies als dynamische Konstante angesehen? Ich weise ihm nur einen String zu.
Antworten:
Ihr Problem ist, dass Sie der Konstante jedes Mal, wenn Sie die Methode ausführen, einen neuen Wert zuweisen. Dies ist nicht zulässig, da dadurch die Konstante nicht konstant wird. obwohl die Inhalte der Zeichenfolge gleich sind (für den Moment, jedenfalls), die tatsächliche String - Objekt selbst unterscheidet sich jedes Mal , wenn die Methode aufgerufen wird. Beispielsweise:
def foo
p "bar".object_id
end
foo #=> 15779172
foo #=> 15779112
Wenn Sie Ihren Anwendungsfall erläutern - warum Sie den Wert einer Konstante in einer Methode ändern möchten -, können wir Ihnen möglicherweise bei einer besseren Implementierung helfen.
Vielleicht möchten Sie lieber eine Instanzvariable für die Klasse haben?
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"
Wenn Sie den Wert einer Konstante in einer Methode wirklich ändern möchten und Ihre Konstante ein String oder ein Array ist, können Sie die #replace
Methode "betrügen" und verwenden , um das Objekt dazu zu bringen, einen neuen Wert anzunehmen, ohne das Objekt tatsächlich zu ändern:
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
. Es ist einer dieser Fälle, in denen Ruby keinen einfachen Weg hat.
@variable
), keine Konstante. Andernfalls würden Sie DB
jedes Mal neu zuweisen, wenn Sie eine neue Instanz dieser Klasse instanziieren.
Sequel.connect
einer Konstanten mit dem Namen DB zuweisen MÜSSEN . In der Dokumentation heißt es ausdrücklich, dass dies nur eine Empfehlung ist. Das klingt für mich nicht nach einer externen Einschränkung.
Da Konstanten in Ruby nicht geändert werden sollen, rät Ruby Sie davon ab, sie in Teilen des Codes zuzuweisen, die möglicherweise mehrmals ausgeführt werden, z. B. innerhalb von Methoden.
Unter normalen Umständen sollten Sie die Konstante innerhalb der Klasse selbst definieren:
class MyClass
MY_CONSTANT = "foo"
end
MyClass::MY_CONSTANT #=> "foo"
Wenn Sie aus irgendeinem Grund wirklich eine Konstante innerhalb einer Methode definieren müssen (möglicherweise für eine Art von Metaprogrammierung), können Sie Folgendes verwenden 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"
Auch hier const_set
sollten Sie unter normalen Umständen nicht unbedingt darauf zurückgreifen müssen. Wenn Sie nicht sicher sind, ob Sie Konstanten auf diese Weise wirklich zuweisen möchten, sollten Sie eine der folgenden Alternativen in Betracht ziehen:
Klassenvariablen verhalten sich in vielerlei Hinsicht wie Konstanten. Sie sind Eigenschaften einer Klasse und in Unterklassen der Klasse, für die sie definiert sind, zugänglich.
Der Unterschied besteht darin, dass Klassenvariablen modifizierbar sein sollen und daher problemlos internen Methoden zugewiesen werden können.
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"
Klassenattribute sind eine Art "Instanzvariable für eine Klasse". Sie verhalten sich ein bisschen wie Klassenvariablen, außer dass ihre Werte nicht mit Unterklassen geteilt werden.
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"
Und der Vollständigkeit halber sollte ich wahrscheinlich erwähnen: Wenn Sie einen Wert zuweisen müssen, der erst bestimmt werden kann, nachdem Ihre Klasse instanziiert wurde, besteht eine gute Chance, dass Sie tatsächlich nach einer einfachen alten Instanzvariablen suchen.
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 ist jede Variable, deren Name mit einem Großbuchstaben beginnt, eine Konstante, die Sie nur einmal zuweisen können. Wählen Sie eine dieser Alternativen:
class MyClass
MYCONSTANT = "blah"
def mymethod
MYCONSTANT
end
end
class MyClass
def mymethod
my_constant = "blah"
end
end
Konstanten in Ruby können nicht innerhalb von Methoden definiert werden. Siehe zum Beispiel die Hinweise unten auf dieser Seite
Sie können eine Variable nicht mit Großbuchstaben benennen, oder Ruby nimmt an, dass sie eine Konstante ist, und möchte, dass sie ihren Wert konstant hält. In diesem Fall wäre das Ändern ihres Werts ein Fehler, ein "dynamischer Konstantenzuweisungsfehler". Mit Kleinbuchstaben sollte in Ordnung sein
class MyClass
def mymethod
myconstant = "blah"
end
end
Ruby mag es nicht, dass Sie die Konstante innerhalb einer Methode zuweisen, da dies zu einer erneuten Zuweisung führen kann. Einige SO-Antworten vor mir bieten die Alternative, sie außerhalb einer Methode zuzuweisen - aber in der Klasse, die ein besserer Ort ist, um sie zuzuweisen.
Vielen Dank an Dorian und Phrogz, die mich an die Array- (und Hash-) Methode #replace erinnert haben, die "den Inhalt eines Arrays oder Hashs ersetzen kann".
Die Vorstellung, dass der Wert eines CONSTANT geändert werden kann, aber mit einer nervigen Warnung, ist einer der wenigen konzeptionellen Fehltritte von Ruby - diese sollten entweder vollständig unveränderlich sein oder die konstante Idee insgesamt verwerfen. Aus der Sicht eines Codierers ist eine Konstante deklarativ und beabsichtigt, ein Signal an andere, dass "dieser Wert nach der Deklaration / Zuweisung wirklich unveränderlich ist".
Aber manchmal schließt eine "offensichtliche Erklärung" tatsächlich andere, zukünftige nützliche Gelegenheiten aus. Beispielsweise...
Es gibt legitime Anwendungsfälle, in denen der Wert einer "Konstante" möglicherweise wirklich geändert werden muss: Zum Beispiel das erneute Laden von ARGV aus einer REPL-ähnlichen Eingabeaufforderungsschleife und das erneute Ausführen von ARGV über weitere (nachfolgende) OptionParser.parse! Anrufe - voila! Verleiht "Befehlszeilenargumenten" ein völlig neues dynamisches Dienstprogramm.
Das praktische Problem besteht entweder in der vermuteten Annahme, dass "ARGV eine Konstante sein muss", oder in der eigenen Initialisierungsmethode von optparse, die die Zuordnung von ARGV zur Instanz var @default_argv für die nachfolgende Verarbeitung fest codiert - dieses Array (ARGV) wirklich sollte ein Parameter sein, der gegebenenfalls eine erneute Analyse und Wiederverwendung fördert. Eine ordnungsgemäße Parametrisierung mit einem geeigneten Standard (z. B. ARGV) würde die Notwendigkeit vermeiden, das "konstante" ARGV jemals zu ändern. Nur ein paar Gedanken wert ...