问题: 使用Ruby可以很容易地将自定义方法添加到现有类中,但是如何添加自定义属性?这是我想要做的一个例子:
myarray = Array.new();
myarray.concat([1,2,3]);
myarray._meta_ = Hash.new(); # obviously, this wont work
myarray._meta_['createdby'] = 'dreftymac';
myarray._meta_['lastupdate'] = '1993-12-12';
## desired result
puts myarray._meta_['createdby']; #=> 'dreftymac'
puts myarray.inspect() #=> [1,2,3]
目标是以这样的方式构造类定义,使得在上面的示例中不起作用的东西将按预期工作。
更新(澄清问题)原始问题中遗漏了一个方面:添加“默认值”也是一个目标,通常会在中设置初始化类的方法。
更新:(为什么这样做)通常,创建一个继承自Array(或任何你想要模拟的内置类)的自定义类非常简单。这个问题源自一些“仅测试”代码,并不是试图忽略这种普遍接受的方法。
答案 0 :(得分:2)
财产不仅仅是一个吸气者和一个二传手吗?如果是这样,你不能这样做:
class Array
# Define the setter
def _meta_=(value)
@_meta_ = value
end
# Define the getter
def _meta_
@_meta_
end
end
然后,你可以这样做:
x = Array.new
x._meta_
# => nil
x._meta_ = {:name => 'Bob'}
x._meta_
# => {:name => 'Bob'}
这有帮助吗?
答案 1 :(得分:1)
回想一下,在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>
对于一个为访问者进行默认初始化的简单方法,我们基本上需要reimplement attr_accessor
ourselves:
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"}