【问题标题】:When to use self in Model?什么时候在模型中使用 self?
【发布时间】:2012-06-04 00:43:21
【问题描述】:

问题:我什么时候需要在 Rails 的模型中使用 self?

我的一个模型中有一个set 方法。

class SomeData < ActiveRecord::Base
  def set_active_flag(val)
    self.active_flag = val
    self.save!
  end
end

当我这样做时,一切正常。但是,当我这样做时:

class SomeData < ActiveRecord::Base
  def set_active_flag(val)
    active_flag = val
    save!
  end
end

active_flag 值不会改变,而是静默失败。有人可以解释吗?

我找不到任何重复项,但如果有人找到也可以。

【问题讨论】:

标签: ruby-on-rails ruby-on-rails-3 ruby-on-rails-3.1 self


【解决方案1】:

当您对调用该方法的实例执行操作时,您使用 self。

使用此代码

class SocialData < ActiveRecord::Base
  def set_active_flag(val)
    active_flag = val
    save!
  end
end

您正在定义一个名为 active_flag 的全新作用域局部变量,将其设置为传入的值,它不与任何东西相关联,因此当方法结束时它会立即被丢弃,就像它从未存在一样。

self.active_flag = val

但是告诉实例修改它自己的名为 active_flag 的属性,而不是一个全新的变量。这就是它起作用的原因。

【讨论】:

  • "当您对调用该方法的实例执行操作时,您使用 self。"这不太准确。在没有self 的情况下,在当前对象上调用(非设置器)方法也可以正常工作。 (直接)设置实例变量也是如此 - 事实上你不能使用self 来做到这一点(无论如何都没有反射)。
  • 它不引用实例变量真是太奇怪了……与 Java 的另一个区别。感谢您的回答。
  • @varatis:由于变量没有在 ruby​​ 中声明,创建变量的唯一方法就是给它赋值。 Java 可以判断foo = bar 是分配给局部变量还是实例变量,因为它可以只查看是否已声明具有该名称的局部变量。 Ruby 不能这样做,因为没有声明。你可以说“如果存在就调用 setter 方法 - 否则创建一个局部变量”,但是你会遇到method_missing 的麻烦。另请注意,self.foo = bar 调用了一个名为 foo= 的方法,它不会(直接)设置实例变量。
  • @varatis 另外,实例变量是@var_name
  • 谁能解释原因
【解决方案2】:

这是由于范围界定而发生的。当您在方法内部并尝试像这样设置一个新变量时:

class SomeData < ActiveRecord::Base
  def set_active_flag(val)
    active_flag = val
  end
end

您正在创建一个位于 set_active_flag 内部的全新变量。一旦执行完毕,它就会消失,不会以任何方式改变self.active_flag(实际的实例变量)。

但是(这让我很困惑):当您尝试在 ruby​​ 中读取一个实例变量时,如下所示:

class SomeData < ActiveRecord::Base
  def whats_my_active_flag
    puts active_flag
  end
end

您实际上会得到 self.active_flag(实际的实例变量)返回。


原因如下:

Ruby 将尽其所能避免返回 nil

  1. 它最初询问“active_flag 是否存在于whats_my_active_flag 的范围内?
  2. 它搜索并意识到答案是“否”,因此它跳一级,到 SomeData 的实例
  3. 它再次问同样的问题:“active_flag 是否存在于这个范围内?
  4. 答案是“是的”,所以它说“我有东西要给你”,然后它就会返回!

但是,如果您在whats_my_active_flag 中定义active_flag,然后请求它,它会再次执行这些步骤:

  1. 它询问“active_flag是否存在于whats_my_active_flag的范围内?
  2. 答案是“是的”,所以它返回那个值

在任何一种情况下,它都不会更改 self.active_flag 的值,除非您明确告诉它这样做。

描述这种行为的一个简单方法是“它不想让你失望”并返回nil——所以它会尽最大努力找到它可以找到的任何东西。

同时,“它不想弄乱您不打算更改的数据”,因此它不会改变实例变量本身。

希望这会有所帮助!

【讨论】:

  • 很好的解释。我一直在寻找一个小时试图弄清楚为什么我需要使用 self 来设置值但可以跳转到不同的方法并使用看似局部变量的读取而不使用 self
  • 很好的解释!当您读取写入变量时,我也对差异感到困惑
  • 这样做的目的是什么?这是一种糟糕的语言设计吗?
【解决方案3】:

这是为了确保您使用的是 setter 方法而不是新变量的范围。这是一个 Ruby 和 AR 的使用细节,经常让人绊倒(另一个是(错误)使用实例变量)。

注意已经有update_attributes! 虽然我理解抽象的愿望。

还有toggle!,这可能会更好,这取决于您与标志的界面。

【讨论】:

  • 我可能在这里遇到了一个悬空修饰符问题......你是说“foo = bar”创建一个局部变量的事实是一个AR实现细节吗?
  • @sepp2k 这没有任何意义,但我的编辑并不清楚。
【解决方案4】:

当使用active_flag = val ruby​​ 以为你定义了一个局部变量,最好的方法是self.active_flag = val,如果你知道了,希望你知道send(:active_flag=, val) 也可以。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-07
    • 1970-01-01
    • 1970-01-01
    • 2012-05-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多