-1

我有一个基本控制器

class Base
  @@var = 'base'

  def self.result
    @@var
  end
  def self.result=(var)
    @@var = var
  end

  def do_sth
    // do something here
  end
end

和 2 个子类

class A < Base
  Base.result = 'a'
end

class B < Base
  Base.result = 'b'
end

当访问 url_a 转到class A,然后我访问 url_b 转到class B,它们都工作正常。

但是当我切换回 url_a 时,Base.result仍然返回b,为什么?


编辑
我已更改@@var@var,我得到了相同的结果。

4

1 回答 1

1

这是@lurker 在问题评论中提到的内容的扩展。无论您使用类变量还是类级实例变量,您都会看到相同的行为。

但是,当您使用这两种类型的变量时,幕后发生的事情是不同的。

案例1:类变量(即@@var

类变量的值是在类声明期间设置的,即在读取 Ruby 源代码时只发生一次。

这里有两件事要记住:

  • Rails 遵循延迟加载,即它在第一次需要时加载一个类的定义。
    • 所以当你第一次点击url_aclass A被加载,即它的源被解析
    • class B尚未加载。它稍后会在您点击时加载url_b
  • 解析 Ruby 源文件时,任何函数之外的所有代码都会立即执行。所以Base.result =方法调用是在加载两个类时执行的。

所以步骤的顺序是:

  • 在对 的调用中url_aclass A被解析并Base.result = a设置@@vara
  • 然后在对 , 的调用中url_bclass b解析并Base.result = b设置@@varb,并且对于所有后续调用都保持不变。

以下代码片段可能有助于理解第二点:

irb(main):033:0> class ParseTest
irb(main):034:1> @@time_now = Time.now
irb(main):035:1> def speak_time
irb(main):036:2> puts @@time_now.to_s
irb(main):037:2> end
irb(main):038:1> end
=> nil
irb(main):039:0> pt = ParseTest.new
=> #<ParseTest:0x007f80758514c8>
irb(main):040:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):041:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):042:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):043:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):044:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):045:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):046:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):047:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):048:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):049:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):050:0> pt.speak_time
2014-09-18 23:15:15 +0530
=> nil
irb(main):051:0> class ParseTest2 < ParseTest
irb(main):052:1> @@time_now = Time.now
irb(main):053:1> end
=> "2014-09-18T23:16:41.911+05:30"
irb(main):054:0> pt.speak_time
2014-09-18 23:16:41 +0530
=> nil
irb(main):055:0> 

如您所见,在ParseTest类定义被解析一次后,@@time_now后续的任何puts. time 的值是解析源代码时的值。

然而,当我定义了ParseTest2子类并解析了它的代码时,同一个类变量被赋予了一个新的时间值。当我使用基类的相同旧对象打印它时,会反映这个新值。

这也是您的代码中发生的事情。

案例2:类级实例变量(即@var在任何实例函数之外的类定义中) 现在,如果您在类定义中(即在任何函数之外)使用实例变量而不是类变量,那将是非常不同的。这种情况可能看起来有点令人困惑,因此请阅读并重新阅读以下代码片段,如果它在第一次让您感到困惑时。

irb(main):089:0> class Base
irb(main):090:1>   @time_now = Time.now
irb(main):091:1> 
irb(main):092:1*   def self.time_now=(time)
irb(main):093:2>     @time_now = time
irb(main):094:2>   end
irb(main):095:1> 
irb(main):096:1*   def self.time_now
irb(main):097:2>     puts @time_now.to_s
irb(main):098:2>   end
irb(main):099:1> end
=> nil
irb(main):100:0> class A < Base
irb(main):101:1>   Base.time_now = Time.now
irb(main):102:1> end
=> "2014-09-18T23:33:26.514+05:30"
irb(main):103:0> Base.time_now
2014-09-18 23:33:26 +0530
=> nil
irb(main):104:0> A.time_now

=> nil
irb(main):105:0> A.time_now = Time.now
=> "2014-09-18T23:34:27.093+05:30"
irb(main):106:0> A.time_now
2014-09-18 23:34:27 +0530
=> nil
irb(main):107:0> Base.time_now
2014-09-18 23:33:26 +0530
=> nil
irb(main):108:0> 

类级别的实例变量是私有的class。它不会在继承期间传递/共享。所以继承层次结构中的每个类都有自己的一组实例变量。但是,这些方法确实会被传递,并且这些方法作用于调用它们的类的实例变量。所以根据你调用time_now=setter方法的类,设置对应的实例变量。

在您的情况下,您总是指的是Base类的实例变量。因此,与前面案例中描述的相同的一组步骤发生

  • 在对,的调用中url_aclass A被解析并Base.result = a设置@varBasea
  • 然后在对 , 的调用中url_bclass b解析并Base.result = b设置@varBasetob并且对于所有后续调用仍然如此。
于 2014-09-18T18:17:50.267 回答