【问题标题】:Ruby self. vs @ in initialize红宝石自己。 vs @ 初始化
【发布时间】:2015-12-17 18:50:58
【问题描述】:

Ruby 中的新功能,试图弄清楚为什么您会选择一种变体而不是另一种。你能写一个比另一个更受欢迎的例子吗?

class User
    attr_accessor :username
    def initialize(username)
       self.username = username
    end
end

class User
    attr_accessor :username
    def initialize(username)
        @username = username
    end
end

【问题讨论】:

  • 这是一个基于意见的问题,第二个版本似乎更简洁,也不需要定义attr_accessor - 因此,我猜它更可取

标签: ruby


【解决方案1】:

它们是一样的。

self.username = 调用 attr_accessor 定义的username= 函数。该函数如下所示:

def username=(value)
  @username = value
end

如您所见,它与您提到的“替代方案”相同。

EDIT 使用访问器(即调用 attr_accessor/reader/etc 定义的函数)明显快于其他形式的访问。 cmets 中有一些链接对此进行了详细说明。

【讨论】:

  • 并且,即使attr_accessor没有定义也可以使用@usernameself.username只有在attr_accessor被定义的情况下才能使用
  • 值得注意的是,attr_accessor 生成的方法的基准测试往往比手动编写的方法快得多。如果可能,请使用生成器。如有必要,请自己编写。
  • 所以如果你有一个 attr_accessor 是通过@或self调用它更好吗?还是根本没有区别?
  • @AlexAvlonitis 使用访问器通常更快。以下是一些解释/辩论原因的讨论:12
  • 实际上,您错过了第 2 行中的尾随逗号。两段代码 100% 相同,因为尾随逗号会导致 Ruby 等待 @ 的第二个参数987654331@。第二个参数是 def 的返回值,它是一个符号,表示正在定义的方法的名称 (:initialize)。因此,def 将定义initialize 方法,但随后attr_accessor立即 用一个名为@initialize 的实例变量的getter 覆盖该方法。因此,两个 sn-ps 最终都会使用完全相同的四种方法:usernameusername=initialize、...
【解决方案2】:

一个例子是当您需要只读属性时。像这样的:

class ProjectProcessor
  attr_reader :project # attr_reader, not attr_accessor

  def initialize(project)
    @project = project
  end

  # more code
end

由于attr_reader 没有定义setter 方法,因此一旦创建处理器,外部代码就不能更改project(无论如何,这不是一种方便的方式)。

【讨论】:

  • 不过,您可以使用private 作家实现同样的目标。
  • @JörgWMittag:是的,但我发现我在初始化程序中设置一个属性并且之后不更改它是很常见的。为此创建一个私人作家似乎......有点太麻烦了。
【解决方案3】:

由于第 2 行中的逗号,这两种情况完全相同:逗号表示您将 两个 参数传递给 attr_accessor,而不是一个。

第二个参数是什么?嗯,它当然是逗号后面的内容,即def 表达式。在旧版本的 Ruby 中,方法定义表达式的返回值是实现定义的(在某些实现中它返回 nil,在某些方法的编译字节码中),但在当前版本的 Ruby 中,@ 的返回值987654324@ 表达式被标准化为Symbol,表示方法的名称为defined。因此,在这种情况下,def 表达式的计算结果为 :initialize

现在,因为 Ruby 是一门严格的语言,参数在传递之前会被评估,这意味着 def initialize 被评估首先,它定义了一个带有一个参数的 initialize 方法。 p>

但是,紧接着,attr_accessor 会被两个参数调用,:username:initialize,因此,attr_accessor 将创建四个方法:username=usernameinitialize= , 和initialize,从而用没有参数的方法覆盖我们刚刚定义的方法。

这就是两个示例相同的原因:虽然两个 initialize 方法最初不同,但它们立即被相同的方法覆盖。

(我想,从技术上讲,你可以观察到差异,如果你设法在正确的时间从不同的线程调用initialize只是,在第一次定义之后直接但是在它被覆盖之前。不过,这是一个非常小的窗口。)

请注意,您发布的代码会生成一个警告:

user.rb:2: warning: method redefined; discarding old initialize
user.rb:3: warning: previous definition of initialize was here

你可以在行号中看到严格评估参数的效果:它抱怨该方法在第 2 行被覆盖,但之前在第 3 行定义,实际上在 2 之后但被评估 它之前。

这显示了我一遍又一遍地写的东西:你实际上应该阅读这些警告。他们被放在那里是有原因的。

【讨论】:

  • 逗号打错了抱歉。我只复制了一部分作为问题,逗号滑入
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-22
  • 1970-01-01
  • 2012-09-01
  • 2019-08-09
相关资源
最近更新 更多