什么时候在模型中使用self?


74

问题:我何时需要在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值不会更改,而是会静默失败。有人可以解释吗?

我找不到任何重复项,但是如果有人发现一个重复项也很好。


Answers:


70

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

有了这个代码

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

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

self.active_flag = val

但是,告诉实例修改其自己的称为active_flag的属性,而不是全新的变量。这就是为什么它起作用。


8
“当您对调用该方法的实例执行操作时,您将使用self。” 那不是很准确。不使用,在当前对象上调用(非设置器)方法就可以正常工作self。设置实例变量也是如此(直接)-实际上,您不能使用self(无论如何都没有反射)来做到这一点。
sepp2k 2012年

它没有引用实例变量是多么奇怪……与Java的另一个区别。感谢你的回答。
瓦拉蒂斯

3
@varatis:由于变量不是用ruby声明的,因此创建变量的唯一方法是为其分配某些内容。Java可以判断foo = bar是分配给局部变量还是实例变量,因为它可以查看是否已声明具有该名称的局部变量。Ruby无法做到这一点,因为没有声明。您可以说“如果存在则调用setter方法-否则创建一个局部变量”,但随后您就遇到了麻烦method_missing。另请注意,self.foo = bar调用名为的方法时foo=,它不会(直接)设置实例变量。
sepp2k 2012年

1
@varatis另外,实例变量是@var_name
戴夫·牛顿

1
谁能解释原因
jj_

63

发生这种情况是由于作用域。当您在方法内部时,尝试尝试设置一个新变量,如下所示:

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. 答案是“是”,所以它说“我为你买了东西”,它返回了!

但是,如果你定义active_flagwhats_my_active_flag,然后问它,它经历的步骤再次:

  1. 它问“是否active_flag存在范围内whats_my_active_flag
  2. 答案是“是”,因此它将返回该值

无论哪种情况,除非您明确告知,否则它都不会更改其值self.active_flag

描述此行为的一种简单方法是“它不想让您失望”并返回 nil-因此,它会尽一切可能找到它。

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

希望这可以帮助!


4
很好的解释。我一直在寻找一个小时,试图弄清楚为什么我需要使用self来设置值,但是可以跳转到不同的方法,并且使用看似是局部变量的内容来读取而不使用self
bkunzi01

1
很好的解释!当您读取写入变量时,也让我感到困惑
Mario

1

这是为了确保您使用的是setter方法,而不是为新变量设定范围。这是Ruby和AR用法的详细信息,经常使人们感到烦恼(另一个是实例变量的(滥用)使用)。

请注意,已经有update_attributes!尽管我了解抽象的渴望。

还有切换!,甚至可能更好,具体取决于您与该标志的接口。


我这里可能有一个悬而未决的修饰符问题……您是说“ foo = bar”创建局部变量的事实是AR实现细节吗?
sepp2k 2012年

@ sepp2k真的没有任何意义,但是我的编辑不清楚。
戴夫·牛顿

0

当使用active_flag = valruby认为您定义了一个局部变量时,最好的方法是self.active_flag = val,如果知道了,希望您也知道它send(:active_flag=, val)会起作用。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.