Mass assignment on construction from within ruby [duplicate]

泪湿孤枕 提交于 2019-12-08 00:57:45

问题


Possible Duplicate:
Idiomatic object creation in ruby

Sometimes it's useful to assign numerous of a constructed arguments to instance variables on construction. Other than the obvious method:

def initialize(arg1, arg2, arg3)
  @arg1, @arg2, @arg3 = arg1, arg2, arg3
end

Is there a more concise idiom for achieving the same result? Something like that found in scala for instance:

class FancyGreeter(greeting: String) {
  def greet() = println(greeting)
}

Where in this case the object FancyGreeter has a default constructor that provides assignment for it's passed arguments.


回答1:


In Ruby 1.8, block arguments and method arguments have different semantics: method arguments have binding semantics, block arguments have assignment semantics.

What that means is that when you call a method, the method arguments get bound to the values that you pass in. When you call a block, the values get assigned to the arguments.

So, you can create some pretty crazy looking blocks that way, that seemingly don't do anything:

lambda {|@a|}.call(42)

The block body is empty, but because of the argument assignment semantics, the instance variable @a will be assigned the value 42. It works even crazier:

lambda {|foo.bar|}.call(42)

Yes, attr_writer methods work too. Or what about

foo = {}
lambda {|foo[:bar]|}.call(42)
p foo # => {:bar => 42}

Yup, those too.

And since you can define methods using blocks, you can do this:

class FancyGreeter
  define_method(:initialize) {|@greeting|}
  def greet; puts @greeting end
end

or even

class FancyGreeter
  attr_accessor :greeting
  define_method(:initialize) {|self.greeting|}
  def greet; puts greeting end
end

However, I wouldn't recommend this for two reasons:

  • Not many Rubyists know this, be kind to the people who have to maintain the code after you.
  • In Ruby 1.9 and onwards, block argument semantics are gone, blocks also use method argument semantics, therefore this does no longer work.



回答2:


I suppose you could do....

def initialize *e
  @a, @b, @c = e
end



回答3:


I don't know about "better" but there are varying levels of 'clever':

def initialize args={}
  args.each do |key, value|
    instance_variable_set "@#{key}", value
  end
end

But "clever" is usually dangerous when you program :-)


Edit: Given the edited question, I'll add this:

Class PickMe
  def initialize say="what?"
    @say = say
  end
end

Just because I don't know if you're aware of default options. Otherwise, think of the value of self-documenting code. A cleanly-written 'initialize' method is priceless.




回答4:


It was either Andy Hunt or Dave Thomas who proposed that Ruby should be able to handle this syntax for initializing member variables from constructor arguments:

  def initialize(@a, @b, @c)
     ...
  end

Matz did not accept their proposal; I don't remember why.



来源:https://stackoverflow.com/questions/2268216/mass-assignment-on-construction-from-within-ruby

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!