Ruby 元编程:动态实例变量名

Posted

技术标签:

【中文标题】Ruby 元编程:动态实例变量名【英文标题】:Ruby Metaprogramming: dynamic instance variable names 【发布时间】:2011-10-08 04:00:00 【问题描述】:

假设我有以下哈希:

 :foo => 'bar', :baz => 'qux' 

如何动态设置键和值成为对象中的实例变量...

class Example
  def initialize( hash )
    ... magic happens here...
  end
end

...所以我最终在模型中得到以下内容...

@foo = 'bar'
@baz = 'qux'

?

【问题讨论】:

【参考方案1】:

你让我们想哭:)

无论如何,请参阅Object#instance_variable_getObject#instance_variable_set

编码愉快。

【讨论】:

呃是的,我不禁想知道......为什么?什么时候用这个比较合适? 例如,我可能希望对所有控制器都有一个通用的set_entity 回调,并且我不想干扰现有的实例变量def set_entity(name, model); instance_variable_set(name, model.find_by(params[:id])); end;【参考方案2】:

您要查找的方法是instance_variable_set。所以:

hash.each  |name, value| instance_variable_set(name, value) 

或者,更简单地说,

hash.each &method(:instance_variable_set)

如果您的实例变量名称缺少“@”(就像在 OP 的示例中一样),您需要添加它们,所以它更像是:

hash.each  |name, value| instance_variable_set("@#name", value) 

【讨论】:

在 1.9.3 中对我不起作用。我用这个代替hash.each |k,v| instance_variable_set("@#k",v) 又一个喜欢 Ruby 的理由 你能解释一下hash.each &method(:instance_variable_set)中的instance_variable_set方法如何接收它需要的两个参数吗? 知道如何递归地执行此操作吗? (如果输入哈希中有多个级别)【参考方案3】:
h =  :foo => 'bar', :baz => 'qux' 

o = Struct.new(*h.keys).new(*h.values)

o.baz
 => "qux" 
o.foo
 => "bar" 

【讨论】:

这很有趣...第二个链式.new() 到底在做什么? @Andrew: Struct.new 基于哈希键创建一个新类,然后第二个new 使刚刚创建的类的第一个对象,将其初始化为哈希值.见ruby-doc.org/core-1.8.7/classes/Struct.html 这实际上是一个非常好的方法,因为这几乎就是 Struct 的用途。 或使用OpenStruct。 require 'ostruct'; h = :foo => 'foo'; o = OpenStruct.new(h); o.foo == 'foo' 我必须将我的键映射到符号:Struct.new(*hash.keys.map |str| str.to_sym ).new(*hash.values)【参考方案4】:

您还可以使用send 来防止用户设置不存在的实例变量:

def initialize(hash)
  hash.each  |key, value| send("#key=", value) 
end

当你的类中有一个像 attr_accessor 这样的 setter 用于你的实例变量时,使用 send

class Example
  attr_accessor :foo, :baz
  def initialize(hash)
    hash.each  |key, value| send("#key=", value) 
  end
end

【讨论】:

以上是关于Ruby 元编程:动态实例变量名的主要内容,如果未能解决你的问题,请参考以下文章

谈谈Ruby中的类变量

一篇文章带你搞清Ruby对象模型

ruby中各种变量范围之间的区别

Ruby 类实例变量与类变量

Ruby的attr_accessor如何产生类变量或类实例变量而不是实例变量?

编程规范