【问题标题】:Ruby is storing classes inside of local variables instead of instance variablesRuby 将类存储在局部变量而不是实例变量中
【发布时间】:2012-06-25 21:32:39
【问题描述】:

为什么 Ruby 强迫我在局部变量而不是实例变量中实例化/存储这些类?

在我更改代码以使其正常运行之前,我有这个:

require 'test/unit'
require 'converter'

class TestConverter < Test::Unit::TestCase

  @cv = Convert.new

  def test_celsius
    assert_equal(100.0, @cv.celsius(212))
    assert_equal(0.0, @cv.@celsius(32))
  end

  def test_fahrenheit
    assert_equal(212.0, @cv.fahrenheit(100))
    assert_equal(32.0, @cv.fahrenheit(0))
  end

end

抛出此错误:

% ruby -I. converter_test.rb                                                  ✭
Run options: 

# Running tests:

EE

Finished tests in 0.000616s, 3246.7638 tests/s, 0.0000 assertions/s.

  1) Error:
test_celsius(TestConverter):
NoMethodError: undefined method `celsius' for nil:NilClass
    converter_test.rb:9:in `test_celsius'

  2) Error:
test_fahrenheit(TestConverter):
NoMethodError: undefined method `fahrenheit' for nil:NilClass
    converter_test.rb:14:in `test_fahrenheit'

2 tests, 0 assertions, 0 failures, 2 errors, 0 skips

我决定尝试在每个方法中实例化类(Convert)并且成功了:

require 'test/unit'
require 'converter'

class TestConverter < Test::Unit::TestCase

  #@cv = Convert.new
  #instantiated the class in each method instead of here

  def test_celsius
    cv = Convert.new
    assert_equal(100.0, cv.celsius(212))
    assert_equal(0, cv.celsius(32))
  end

  def test_fahrenheit
    cv = Convert.new
    assert_equal(212, cv.fahrenheit(100))
assert_equal(32, cv.fahrenheit(0))
end
end
ddouglas@coders:~/Develop/davincicoders$ ruby -I. converter_test.rb
Run options: 

# Running tests:

..

Finished tests in 0.001894s, 1055.9149 tests/s, 2111.8298 assertions/s.

2 tests, 4 assertions, 0 failures, 0 errors, 0 skips

为什么 Ruby 在第一次尝试时不能将实例变量识别为对象?

【问题讨论】:

  • 因为这不是在 Ruby 中声明实例变量的方式。在类声明中,您在类中,而不是实例中,因此 @variable 不是您认为的那样。
  • 所以基本上它不起作用,因为我在方法之外定义了实例。如果我在初始化方法中完成它,它会起作用的。对?嗯……在测试中这似乎不起作用……好吧,我想我明白了。谢谢大家。
  • 哪个是最有效的代码?使用@@class_var 声明还是按照我上面的方式进行?

标签: ruby unit-testing instance-variables local-variables


【解决方案1】:

在测试之外声明 @cv 使其成为 TestConverter 的实例变量 - 而不是 TestConverter 的实例!

可能最简单的解决方法是将其设为类变量:@@cv

如果您仍然感到困惑,请考虑以下示例:

class Foo
  @x = 3
  def initialize
    @y = 4
  end
end

puts Foo.instance_variables
puts Foo.new.instance_variables

【讨论】:

    【解决方案2】:

    这常常让人们(包括我)感到困惑,因为它的工作方式与您对其他语言的期望不同。我将尝试用一个 Java 示例来说明:

    class Foo
      @bar = 42
    end
    

    等价于

     public class Foo {
          private int bar = 42;
     }
    

    但实际上大致相当于

     public class Foo {
          private static int bar = 42;
     }
    

    在你的 IRB 中尝试一下:

    class Foo
      attr_accessor :bar
      @bar = 42
      class << self
        attr_accessor :bar
      end
    end
    Foo.bar # => 42
    Foo.new.bar # => nil
    

    那为什么会这样呢?在 Ruby 中,一切都是对象!所以任何类(例如Foo)都是Class 类的一个实例(我知道这听起来很混乱)。 class Foo; end 之间的任何内容都在 Class 类的实例 Foo 的范围内执行。

    这绝不是一个完整的解释,所以你应该仔细阅读细节。

    【讨论】:

      【解决方案3】:
      assert_equal(0.0, @cv.@celsius(32))
      

      不应该这样

      assert_equal(0.0, @cv.celsius(32))
      

      【讨论】:

      • 是的,如果它实际上是一个实例变量......但它不是 OP 认为的实例变量。最终这并没有回答真正的问题,这是对 Ruby 的误解。
      • @David "OP" 的意思是“原始海报”。这是一种与性别无关的简短方式来指代提出问题的人。
      猜你喜欢
      • 1970-01-01
      • 2010-10-28
      • 2020-04-07
      • 1970-01-01
      • 2014-09-21
      • 1970-01-01
      • 2016-12-19
      • 1970-01-01
      相关资源
      最近更新 更多