为什么"instance.send(:initialize, *args, **kwargs, &block)"只在类#new中失败?



我已经纠结了好一阵子了。看看这个:

class SuperClass
  def self.new(*args, **kwargs, &block)
    i = allocate()
    # Extra instance setup code here
    i.send(:initialize, *args, **kwargs, &block)
    return i
  end
end
class Test < SuperClass
  def initialize
    puts "No args here"
  end
end

SuperClass基本上"重新实现"默认的new方法,以便在initialize之前进行一些额外的初始化。

现在,下面的代码运行正常:

t = Test.allocate
t.send(:initialize, *[], **{}, &nil)

但是,这不是:

t = Test.new
ArgumentError:参数数目错误(1 for 0)From (pry):7:in ' initialize'

SuperClass这一行失败:

i.send(:initialize, *args, **kwargs, &block)

但显然它只有在new方法中调用时才会失败。我已经确认args == [], kwargs == {}block == nil .

有人能解释一下吗?


Ruby版本:

ruby 2.2.3p173 (2015-08-18 revision 51636) [x86_64-linux]

请不要建议我不要重载Class.new。我知道我可以使用Class.inheritedClass.append获得相同的结果。这个问题只是关于为什么调用initialize失败。

让我们看一个更简单的例子,特别是因为这个问题并不像问题和它的标题那样具体,但是你自己看看。

def m   # takes no arguments
end
m(**{}) # no argument is passed
h = {}
m(**h)  # an argument is passed => ArgumentError is raised

这个不一致是在2.2.1中引入的,目的是修复一个涉及**{}的分段错误(Bug #10719)。将特殊情况下的**{}提交为不传递参数。其他方式,如**Hash.newh={};**h,仍然传递空哈希作为参数。

以前的版本始终会引发ArgumentError (demo)。我可能是错的,但我相信这是有意的行为。然而,这可能是也可能不是那个人真正想要的。因此,如果您认为双散列空散列不应该传递参数(如目前的**{}),因此工作方式类似于散列空数组,那么存在一个开放的问题(Bug #10856)。它还提到了这个相对较新的不一致

一个简单的*args将捕获包括关键字参数在内的所有参数,以防您不需要在new方法中单独引用kwargs:

class SuperClass
  def self.new(*args, &block)
    i = allocate
    # Extra instance setup code here
    i.send(:initialize, *args, &block)
    i
  end
end

最新更新