Ruby动态对象属性,发送vs实例变量集

时间:2013-02-04 21:50:06

标签: ruby metaprogramming

假设我有一个简单的对象。对于我想接受的属性,我有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因伪造的初始数据被提出的可能性。但是,我在这里忽略了其他任何因素,尤其是性能和安全方面的考虑因素吗?

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

3 个答案:

答案 0 :(得分:5)

我的回答是使用发送。

原因:即使访问者不存在,也会使用instance_variable_set创建实例变量。正如您所提到的,使用send,您将获得NoMethodError。

答案 1 :(得分:2)

您可以使用try代替send。如果找不到该方法,send将返回NoMethod错误,如果找不到方法,try将返回 nil try不能用于实例方法。

示例:

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

try(k)
#=> nil

try(k, v)
#=> nil

但应使用方法instance_variable_setinstance_variable_get

设置或读取实例变量

示例

# k is a string or symbol, v a value
instance_variable_set("@#{k}", v)
instance_variable_get("@#{k}")

答案 2 :(得分:1)

通常的做法是使用发送。

首先,instance_variable_set将创建许多额外的实例变量。

然后,send使用访问器方法。当其中一个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

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