5

假设我有一个简单的对象。我有attr_accessor我想要接受的属性,并且我希望能够用对象的散列进行初始化:

class Example
  attr_accessor :foo, :bar

  def initialize( attributes = {} )
    attributes.each do |k,v|
      ...
    end
  end
end

在上面的 attributes.each 块中,最好使用send,如下所示:

send "#{k}=", v

或使用instance_variable_set,像这样:

instance_variable_set "@#{k}".to_sym, v

...或者完全做其他事情?

我能想到的区别是:

  • 如果在某些时候我用 setter 方法替换了其中一个,则使用send会更加一致。attr_accessors

  • 如果传递了意外的值,使用send会引发NoMethodError

send由于这些原因,我倾向于- 我实际上有点喜欢NoMethodError因虚假初始化数据而被提出的可能性。但是,我在这里是否忽略了其他任何因素,尤其是性能和安全考虑?

我很欣赏任何见解。谢谢!

4

3 回答 3

5

我的答案是使用发送。

原因:即使访问器不存在,也会使用 instance_variable_set 创建实例变量。正如你所提到的,使用 send 你会得到一个 NoMethodError。

于 2013-02-04T22:54:21.077 回答
2

您可以使用try而不是send. send如果没有找到方法将返回 NoMethod 错误,如果没有找到方法try将返回niltry不能用于实例方法。

例子:

# k is a string or symbol, v a parameter value

try(k)
#=> nil

try(k, v)
#=> nil

但是实例变量应该使用方法设置或读取,instance_variable_set并且instance_variable_get

例子

# k is a string or symbol, v a value
instance_variable_set("@#{k}", v)
instance_variable_get("@#{k}")
于 2016-01-26T11:22:34.043 回答
1

通常的做法是使用发送。

首先,instance_variable_set会创建很多额外的实例变量。

然后,发送使用访问器方法。重新定义 attr 访问器之一时非常有用,例如:

attr_accessor :foo

def bar=
  # something's happening here
end

为了避免NoMethodError你应该使用respond_to?方法:

attributes.each do |k, v|
  send("#{k}=", v) if respond_to?(k)
end

也可以在手动初始化或使用 gems 之前裁剪额外的属性,例如gem 'strong_parameters'

于 2016-01-26T11:58:07.530 回答