回想一下,在 Ruby 中,您无法访问该实例之外的属性(实例变量)。您只能访问实例的公共方法。
您可以使用attr_accessor
为充当您描述的属性的类创建方法:
irb(main):001:0> class Array
irb(main):002:1> attr_accessor :_meta_
irb(main):003:1> end
=> nil
irb(main):004:0>
irb(main):005:0* x = [1,2,3]
=> [1, 2, 3]
irb(main):006:0> x._meta_ = Hash.new
=> {}
irb(main):007:0> x._meta_[:key] = 'value'
=> "value"
irb(main):008:0>
对于为访问器进行默认初始化的简单方法,我们需要基本上重新实现attr_accessor
自己:
class Class
def attr_accessor_with_default accessor, default_value
define_method(accessor) do
name = "@#{accessor}"
instance_variable_set(name, default_value) unless instance_variable_defined?(name)
instance_variable_get(name)
end
define_method("#{accessor}=") do |val|
instance_variable_set("@#{accessor}", val)
end
end
end
class Array
attr_accessor_with_default :_meta_, {}
end
x = [1,2,3]
x._meta_[:key] = 'value'
p x._meta_
y = [4,5,6]
y._meta_[:foo] = 'bar'
p y._meta_
可是等等!输出不正确:
{:key=>"value"}
{:foo=>"bar", :key=>"value"}
我们围绕文字哈希的默认值创建了一个闭包。
更好的方法可能是简单地使用一个块:
class Class
def attr_accessor_with_default accessor, &default_value_block
define_method(accessor) do
name = "@#{accessor}"
instance_variable_set(name, default_value_block.call) unless instance_variable_defined?(name)
instance_variable_get(name)
end
define_method("#{accessor}=") do |val|
instance_variable_set("@#{accessor}", val)
end
end
end
class Array
attr_accessor_with_default :_meta_ do Hash.new end
end
x = [1,2,3]
x._meta_[:key] = 'value'
p x._meta_
y = [4,5,6]
y._meta_[:foo] = 'bar'
p y._meta_
现在输出是正确的,因为Hash.new
每次检索默认值时都会调用它,而不是每次都重用相同的文字哈希。
{:key=>"value"}
{:foo=>"bar"}