【问题标题】:Throw exception when re-assigning a constant in Ruby?在Ruby中重新分配常量时抛出异常?
【发布时间】:2011-03-02 16:54:15
【问题描述】:

我早就意识到 Ruby 中的“常量”(即大写的变量名)真的不是常量。像其他编程语言一样,对对象的引用是唯一存储在变量/常量中的东西。 (边栏:Ruby 确实具有“冻结”被修改的引用对象的功能,据我所知,这在许多其他语言中是不具备的。)

所以这是我的问题:当您将值重新分配给常量时,您会收到如下警告:

>> FOO = 'bar'
=> "bar"
>> FOO = 'baz'
(irb):2: warning: already initialized constant FOO
=> "baz"

有没有办法强制 Ruby 抛出异常而不是打印警告?很难弄清楚为什么有时会发生重新分配。

【问题讨论】:

    标签: ruby exception constants


    【解决方案1】:

    不能直接拦截,不行。

    如果您真的需要这样做,我可以想到一个非常肮脏的黑客攻击。您可以将标准错误 IO 重定向到自定义 IO 对象。然后write 方法可以检查正在写入的内容;如果它包含"warning: already initialized constant",则引发,否则将调用转发到标准错误的write

    【讨论】:

    • 这实际上是一种工作。 非常的创意方法,不过 ;)
    • 实际上,这基本上就是您所指的最佳答案所说的......我们一定是在同一时间发布的,因为我发布时没有阅读它。
    【解决方案2】:

    查看Can you ask ruby to treat warnings as errors?,了解在某些情况下如何将警告视为错误。

    否则我猜你必须编写一个自定义方法来分配常量并在已经分配的情况下引发异常。

    如果您知道特定常量发生了重新分配,您还可以在分配之前添加健全性检查。

    【讨论】:

    • 感谢您指出另一个问题——我不会考虑为此使用Kernel.warn。不过,看起来常量重新分配并没有使用它。
    • 我并没有真正尝试过;但我可以想象常量赋值可能是语言本身的一部分,因此可以在 C 代码中处理。在这种情况下,它会更难挂钩,更不用说引发 Ruby 异常了。
    【解决方案3】:

    如果常量在类或模块中,那么你可以freeze类或模块:

    # Normal scenario
    $VERBOSE = true
    class Foo
      BAR = 1
    end
    
    Foo::BAR = 2 # warning: already initialized constant BAR
    # Using freeze
    Foo.freeze
    Foo::BAR = 3
    RuntimeError: can't modify frozen Class
        from (irb):8
        from /Users/agrimm/.rbenv/versions/1.9.3-p194/bin/irb:12:in `<main>'
    

    对于您的场景,您可以冻结 Object,但这听起来有点吓人。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-05-24
      • 1970-01-01
      • 2011-06-27
      • 2011-10-10
      • 1970-01-01
      • 1970-01-01
      • 2011-12-26
      相关资源
      最近更新 更多