这是一种常见的初始化模式:
def initialize(title, val, type)
@title, @val, @type = title, val, type
end
是否有一个等同于“获取每个参数,创建同名属性,并将属性设置为参数值”的快捷方式?
我正在寻找一种无宝石的解决方案。
答案 0 :(得分:4)
你将失去检查错误参数的功能,但可以这样做:
def initialize(*args)
@title, @val, @type = args
end
但如果你反复这样做,那么你的代码就不对了。您最好重新设计API以获取命名参数:
def initialize(title:, val:, type:)
...
end
WhateverClass.new(title: "foo", val: "bar", type: "baz")
答案 1 :(得分:3)
您可以使用上面提到的 Struct 或动态执行以下操作:
class MyClass
def initialize input
input.each do |k,v|
instance_variable_set("@#{k}", v) unless v.nil?
end
end
end
无论如何,如果是我的代码,我将使用Struct。
答案 2 :(得分:2)
以下是你如何做到这一点。将为initialize
的每个参数创建一个实例变量和一个关联的读/写访问器,变量具有相同的名称,前面有@
,每个实例变量都将赋值相关参数。
<强>代码强>
class MyClass
def initialize(< arbitrary parameters >)
self.class.params.each { |v|
instance_variable_set("@#{v}", instance_eval("#{v}")) }
< other code >
end
@params = instance_method(:initialize).parameters.map(&:last)
@params.each { |p| instance_eval("attr_accessor :#{p}") }
class << self
attr_reader :params
end
< other code >
end
示例强>
class MyClass
def initialize(a, b, c)
self.class.params.each { |v|
instance_variable_set("@#{v}", instance_eval("#{v}")) }
end
@params = instance_method(:initialize).parameters.map(&:last)
@params.each { |p| instance_eval("attr_accessor :#{p}") }
class << self
attr_reader :params
end
end
MyClass.methods(false)
#=> [:params]
MyClass.instance_methods(false)
#=> [:a, :a=, :b, :b=, :c, :c=]
m = MyClass.new(1,2,3)
m.a #=> 1
m.b #=> 2
m.c #=> 3
m.a = 4
m.a #=> 4
<强>解释强>
解析类MyClass
时,会为类实例变量@params
分配一个数组,其元素为initialize
的参数。这是可能的,因为在解析代码开始initialize
时创建了方法@params = ...
。
方法Method#parameters用于获取initialize
的参数。对于上面的例子,
instance_method(:initialize).parameters
#=> [[:req, :a], [:req, :b], [:req, :c]]
所以
@params = instance_method(:initialize).parameters.map(&:last)
#=> [:a, :b, :c]
然后我们创建读/写访问器:
@params.each { |p| instance_eval("attr_accessor :#{p}") }
以及@params
的读取访问器,供initialize
使用:
class << self
attr_reader :params
end
创建my_class
的实例MyClass
时,传递给MyClass.new
的参数值将传递给initialize
。然后initialize
循环通过类实例变量@params
并设置每个实例变量的值。在这个例子中,
MyClass.new(1,2,3)
调用initialize(a,b,c)
其中
a => 1
b => 2
c => 3
我们有:
params = self.class.params
#=> [:a, :b, :c]
params.each { |v| instance_variable_set("@#{v}", instance_eval("#{v}")) }
对于params
(:a
)的第一个元素,这是:
instance_variable_set("@a", instance_eval(a) }
是:
instance_variable_set("@a", 1 }
导致@a
被分配1
。
请注意@params
的访问者不是必需的:
class MyClass
def initialize(a, b, c)
self.class.instance_variable_get(:@params).each { |v|
instance_variable_set("@#{v}", instance_eval("#{v}")) }
end
@params = instance_method(:initialize).parameters.map(&:last)
@params.each { |p| instance_eval("attr_accessor :#{p}") }
end
答案 3 :(得分:1)
我喜欢使用args哈希并将每个属性发送到实例。但是,这不会为这些属性创建访问器,因此如果需要,您需要添加这些属性。
def initialize(args)
args.each do |method, value|
self.send("#{method}=", value)
end
end