鉴于以下课程:
class Test
attr_accessor :name
end
当我创建对象时,我想执行以下操作:
t = Test.new {Name = 'Some Test Object'}
目前,名称仍为nil
。这可能吗?
注意:我不想添加初始化程序。
答案 0 :(得分:18)
确定,
我想出了一个解决方案。它使用初始化方法,但另一方面完全按照你想要的那样做。
class Test
attr_accessor :name
def initialize(init)
init.each_pair do |key, val|
instance_variable_set('@' + key.to_s, val)
end
end
def display
puts @name
end
end
t = Test.new :name => 'hello'
t.display
快乐吗? :)
使用继承的替代解决方案。注意,使用此解决方案,您无需显式声明attr_accessor!
class CSharpStyle
def initialize(init)
init.each_pair do |key, val|
instance_variable_set('@' + key.to_s, val)
instance_eval "class << self; attr_accessor :#{key.to_s}; end"
end
end
end
class Test < CSharpStyle
def initialize(arg1, arg2, *init)
super(init.last)
end
end
t = Test.new 'a val 1', 'a val 2', {:left => 'gauche', :right => 'droite'}
puts "#{t.left} <=> #{t.right}"
答案 1 :(得分:9)
正如其他人所说,最简单的方法是定义initialize
方法。如果您不想这样做,可以让您的班级继承自Struct。
class Test < Struct.new(:name)
end
现在:
>> t = Test.new("Some Test Object")
=> #<struct Test name="Some Test Object">
>> t.name
=> "Some Test Object"
答案 2 :(得分:7)
通常的方法是使用必要的操作来执行复杂对象初始化传递块。可以在对象的上下文中评估此块,因此您可以轻松访问所有实例变量,方法等。
继续你的例子
class Test
attr_accessor :name
def initialize(&block)
instance_eval(&block)
end
end
然后
t = Test.new { @name = 'name' }
或
t = Test.new do
self.name = 'name'
# other initialization, if needed
end
请注意,这种方式不需要复杂地更改initialize
方法(实际上是单行方式)。
答案 3 :(得分:1)
如前所述,执行此操作的明智方法是使用Struct
或定义Test#initialize
方法。这正是结构和构造函数的用途。使用与属性相对应的选项哈希是最接近的C#示例,它是一个看似普通的Ruby约定:
t = Test.new({:name => "something"})
t = Test.new(name: "something") # json-style or kwargs
但是在你的例子中,你正在做一些看起来更像是使用=
的变量赋值的东西,所以让我们尝试使用块而不是哈希。 (你也使用Name
这将是Ruby中的常量,我们将改变它。)
t = Test.new { @name = "something" }
很酷,现在让它真正起作用:
class BlockInit
def self.new(&block)
super.tap { |obj| obj.instance_eval &block }
end
end
class Test < BlockInit
attr_accessor :name
end
t = Test.new { @name = "something" }
# => #<Test:0x007f90d38bacc0 @name="something">
t.name
# => "something"
我们创建了一个带有构造函数的类,该构造函数接受一个块参数,该参数在新实例化的对象中执行。
因为你说你想避免使用initialize
,所以我改为覆盖new
并调用super
来获取Object#new
的默认行为。 通常我们会定义initialize
而不是,除了在您的问题中满足特定请求之外,不建议使用此方法。
当我们将一个块传递给BlockInit
的子类时,我们可以做的不仅仅是设置变量......我们基本上只是将代码注入initialize
方法(我们避免写入)。如果您还想要一个initialize
方法来处理其他内容(正如您在评论中提到的那样),您可以将其添加到Test
,甚至不必调用super
(因为我们的更改不是在BlockInit#initialize
,而不是BlockInit.new
)
希望这是一个非常具体和有趣的请求的创造性解决方案。
答案 4 :(得分:0)
您指示的代码是将参数传递到initialize
函数。你肯定要使用initialize
,或者使用更无聊的语法:
test = Test.new
test.name = 'Some test object'
答案 5 :(得分:0)
需要子类Test(这里用自己的方法和初始化器显示),例如:
class Test
attr_accessor :name, :some_var
def initialize some_var
@some_var = some_var
end
def some_function
"#{some_var} calculation by #{name}"
end
end
class SubClassedTest < Test
def initialize some_var, attrbs
attrbs.each_pair do |k,v|
instance_variable_set('@' + k.to_s, v)
end
super(some_var)
end
end
tester = SubClassedTest.new "some", name: "james"
puts tester.some_function
输出:some calculation by james
答案 6 :(得分:0)
你可以这样做。
class Test
def not_called_initialize(but_act_like_one)
but_act_like_one.each_pair do |variable,value|
instance_variable_set('@' + variable.to_s, value)
class << self
self
end.class_eval do
attr_accessor variable
end
end
end
end
(t = Test.new).not_called_initialize :name => "Ashish", :age => 33
puts t.name #=> Ashish
puts t.age #=> 33
一个优点是您甚至不必使用attr_accessor
预先定义实例变量。您可以通过not_called_initialize
方法传递所需的所有实例变量,并在定义getter和setter之外创建它们。
答案 7 :(得分:0)
如果您不想覆盖initialize
,则必须向上移动链并覆盖new
。这是一个例子:
class Foo
attr_accessor :bar, :baz
def self.new(*args, &block)
allocate.tap do |instance|
if args.last.is_a?(Hash)
args.last.each_pair do |k,v|
instance.send "#{k}=", v
end
else
instance.send :initialize, *args
end
end
end
def initialize(*args)
puts "initialize called with #{args}"
end
end
如果您传入的最后一件事是哈希,它将绕过initialize
并立即致电设置者。如果你传递了其他内容,则会使用这些参数调用initialize。