Ruby 使用 instance_eval 对象初始化

Ruby object initialization using instance_eval

考虑以下 class:

class Person
    attr_accessor :first_name

    def initialize(&block)
        instance_eval(&block) if block_given?
    end
end

当我创建一个 Person 实例时,如下所示:

person = Person.new do
    first_name = "Adam"
end

我预期如下:

puts person.first_name

输出"Adam"。相反,它只输出一个空行:first_name 属性的值为 nil。

当我创造一个这样的人时,虽然:

person = Person.new do
    @first_name = "Adam"
end

first_name 属性设置为预期值。

问题是我想在初始化块中使用 attr_accessor,而不是直接使用属性。这能做到吗?

试试这个:

person = Person.new do |obj|
  obj.first_name = "Adam"
end

puts person.first_name

Ruby setter 不能在没有显式接收器的情况下调用,因为局部变量优先于方法调用。

你不需要尝试这样一个过于复杂的例子,下面的例子也行不通:

class Person
  attr_accessor :name
  def set_name(new_name)
    name = new_name
  end
end

只有这样才能:

class Person
  attr_accessor :name
  def set_name(new_name)
    # name = new_name does not call `#name=`
    self.name = new_name
  end
end

对于您的示例,您必须显式调用接收器上的方法:

person = Person.new do
  self.first_name = "Adam"
end

如果代码 运行 启用了警告(即 ruby -w yourprogram.rb) 它响应:"warning: assigned but unused variable - first_name",line-number 指向 first_name = "Adam"。所以 Ruby 将 first_name 解释为变量,而不是方法。正如其他人所说,使用显式接收者:self.first_name.

I want to use the attr_accessor in the initialization block, and not the attributes directly

instance_eval 破坏封装。它使块可以访问实例变量和私有方法。

考虑将人物实例传递到块中:

class Person
  attr_accessor :first_name

  def initialize
    yield(self) if block_given?
  end
end

用法:

adam = Person.new do |p|
  p.first_name = 'Adam'
end
#=> #<Person:0x00007fb46d093bb0 @first_name="Adam">