Dynamic constant assignment

class MyClass
def mymethod
MYCONSTANT = "blah"
end
end

gives me the error:

SyntaxError: dynamic constant assignment error

Why is this considered a dynamic constant? I'm just assigning a string to it.

138603 次浏览

Ruby 中的常量不能在方法内部定义

您的问题在于,每次运行该方法时,都要为该常数赋一个新值。这是不允许的,因为它使常量成为非常量; 即使字符串的 内容是相同的(至少目前是这样) ,每次调用该方法时,实际的字符串 对象本身也是不同的。例如:

def foo
p "bar".object_id
end


foo #=> 15779172
foo #=> 15779112

也许如果您解释一下您的用例ーー为什么要更改方法中常量的值ーー我们可以帮助您实现一个更好的实现。

也许你更愿意在课堂上有个实例变量?

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"

如果你 真的想要改变一个方法中常量的值,而你的常量是一个字符串或者数组,你可以“欺骗”并且使用 #replace方法使得对象在不改变对象的情况下获得一个新的值:

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"

在 Ruby 中,任何名称以大写字母开头的变量都是常量,只能赋值给它一次。选择以下其中之一:

class MyClass
MYCONSTANT = "blah"


def mymethod
MYCONSTANT
end
end


class MyClass
def mymethod
my_constant = "blah"
end
end

因为 Ruby 中的常量不应该被更改,所以 Ruby 不鼓励您在代码的某些部分(比如内部方法)中为它们赋值,因为这些代码可能会被多次执行。

在正常情况下,应该在类本身内部定义常量:

class MyClass
MY_CONSTANT = "foo"
end


MyClass::MY_CONSTANT #=> "foo"

如果出于某种原因,您确实需要在方法内部定义一个常量(可能是为了某种类型的元编程) ,那么您可以使用 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"

但是,在正常情况下,const_set并不是您真正必须求助的东西。如果您不确定 真的是否希望以这种方式赋值给常量,那么您可能需要考虑以下选项之一:

类变量

类变量在许多方面表现得像常量。它们是类的属性,可以在定义它们的类的子类中访问。

区别在于类变量是可修改的,因此可以毫无问题地赋值给内部方法。

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"

类属性

类属性是一种“类的实例变量”。它们的行为有点像类变量,只不过它们的值不与子类共享。

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"

实例变量

为了完整起见,我可能需要提一下: 如果你需要赋值一个只能在你的类被实例化之后才能确定的值,那么你很有可能实际上是在寻找一个普通的实例变量。

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

你不能用大写字母来命名一个变量,否则 Ruby 会假设它是一个常量,并希望它保持它的值不变,在这种情况下,改变它的值将是一个错误,一个“动态常量赋值错误”。小写应该没问题

class MyClass
def mymethod
myconstant = "blah"
end
end

Ruby 不喜欢在方法内部分配常量,因为它有重新分配的风险。在我之前的几个 SO 答案给出了在方法之外赋值的选择——但是在类中,这是一个更好的赋值位置。

非常感谢 Dorian 和 Phrogz 提醒我数组(和 hash)方法 # place,它可以“替换数组或 hash 的内容”

CONSTANT 的值可以更改,但是有一个恼人的警告,这是 Ruby 在概念上的少数错误步骤之一——这些错误步骤要么应该是完全不可变的,要么完全抛弃常量的思想。从编码人员的角度来看,常量是声明性的,是有意为之的,这是向其他人发出的一个信号,表明“这个值在声明/分配之后是真正不可更改的”

但有时,一个“显而易见的宣言”实际上扼杀了其他未来有用的机会。例如..。

的合法用例中,“常量”的值可能真的需要更改: 例如,从类似 REPL 的提示循环重新加载 ARGV,然后通过更多(后续) OptionParser.parse 重新运行 ARGV!电话,瞧!给“命令行 args”一个全新的动态实用程序。

实际问题是 都不是假设“ ARGV 必须是一个常量”,optparse 自己的 initialize 方法中的 或者硬编码将 ARGV 赋值给实例 var@default _ ARGV 进行后续处理——该数组(ARGV)确实应该是一个参数,鼓励在适当的情况下重新解析和重用。适当的参量化,加上适当的默认值(比如 ARGV) ,就可以避免改变“常量”ARGV 的需要。只是一些两个价值的想法..。