动态常量赋值

时间:2011-07-15 19:38:26

标签: ruby

class MyClass
  def mymethod
    MYCONSTANT = "blah"
  end
end

给我错误:

  

SyntaxError:动态常量赋值错误

为什么这被视为动态常数?我只是给它分配一个字符串。

7 个答案:

答案 0 :(得分:125)

您的问题是,每次运行该方法时,都会为常量赋值。这是不允许的,因为它使常数不恒定;即使字符串的内容是相同的(暂时,无论如何),每次调用该方法时,实际字符串 object 本身都是不同的。例如:

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"

如果确实想要在方法中更改常量的值,并且常量是String或Array,则可以“作弊”并使用#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"

答案 1 :(得分:65)

因为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

答案 2 :(得分:26)

在Ruby中,名称以大写字母开头的任何变量都是常量,您只能分配一次。选择以下替代方案之一:

class MyClass
  MYCONSTANT = "blah"

  def mymethod
    MYCONSTANT
  end
end

class MyClass
  def mymethod
    my_constant = "blah"
  end
end

答案 3 :(得分:14)

ruby​​中的常量不能在方法内定义。 See the notes at the bottom of this page, for example

答案 4 :(得分:0)

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

class MyClass
  def mymethod
    myconstant = "blah"
  end
end

答案 5 :(得分:0)

Ruby不喜欢您在方法内部分配常量,因为它有重新分配的风险。在我之前有几个SO答案提供了在方法外部分配它的替代方法,但是在类中,这是分配它的更好位置。

答案 6 :(得分:0)

非常感谢Dorian和Phrogz提醒我有关数组(和哈希)方法#replace的信息,该方法可以“替换数组或哈希的内容。”

关于常量值可以更改但带有令人讨厌的警告的观念,是Ruby少数概念上的失误之一-这些失误应该是完全不变的,或者完全抛弃了不变的想法。从编码者的角度来看,常数是声明性的和有意的,向其他人发出信号,即“一旦声明/分配,此值实际上是不可更改的。”

但有时“明显的声明”实际上会排除其他未来有用的机会。例如...

合法的使用案例中,可能确实需要更改“常量”的值:例如,从类似于REPL的提示循环中重新加载ARGV,然后通过更多方式重新运行ARGV(随后)OptionParser.parse!电话-瞧!为“命令行参数”提供了一个全新的动态实用程序。

实际问题是,并采用optparse自己的initialize方法中的“ ARGV必须为常数”,,该方法硬编码ARGV的分配实例var @default_argv进行后续处理-该数组(ARGV)实际上应该是一个参数,在适当的情况下鼓励重新解析和重用。适当的参数设置以及适当的默认值(例如ARGV)将避免需要更改“恒定” ARGV。仅有价值2美分的想法...