向’self’发送消息会导致调用initialize方法吗?

我有两个classFooBar

 require 'pry-byebug' require 'fileutils' class Foo < Pathname include FileUtils def initialize(path) puts "Inside Foo init..." super puts "Side effect happening..." end def some_method puts "Inside some_method inside Foo..." basename.to_s end end class Bar < Foo end bar = Bar.new('bar') # binding.pry bar.some_method 

这是输出:

 Inside Foo init... Side effect happening... Inside some_method inside Foo... Inside Foo init... Side effect happening... 

正如你所看到的那样,“副作用”正在发生两次。 看一下pry-byebug会话确认:

 Inside Foo init... Side effect happening... From: /Users/max/Dropbox/work/tmp/super_test/foo.rb @ line 23 : 18: class Bar  23: bar.some_method [1] pry(main)> step From: /Users/max/Dropbox/work/tmp/super_test/foo.rb @ line 13 Foo#some_method: 12: def some_method => 13: puts "Inside some_method inside Foo..." 14: basename.to_s 15: end [1] pry(#)> step Inside some_method inside Foo... From: /Users/max/Dropbox/work/tmp/super_test/foo.rb @ line 14 Foo#some_method: 12: def some_method 13: puts "Inside some_method inside Foo..." => 14: basename.to_s 15: end [1] pry(#)> step From: /Users/max/Dropbox/work/tmp/super_test/foo.rb @ line 7 Foo#initialize: 6: def initialize(path) => 7: puts "Inside Foo init..." 8: super 9: puts "Side effect happening..." 10: end 

所以打破它:

  1. 我实例化bar ,它是一个inheritance自FooBar实例。 Bar的超类’ initialize被调用,并且“副作用”发生。 到目前为止,这完全是预期的。
  2. 我在没有它的bar打电话给some_method所以ruby上升到右边并且发现它在Foo里面
  3. Ruby在some_method并找到一个向self发送消息的方法,称为basename
  4. Ruby回到了Foo的’ initialize方法?…

第4步让我完全惊讶。 为什么要向self发送消息会导致initialize方法再次被调用? 这在任何地方记录? 这是预期的吗?

有可能控制这个吗? 或者有条件地检查我是否在initialize方法中,因为我实际上是在实例化一个类,而不是随机地在那里登陆? 例如:

 class Foo < SomeClass def initialize args @args = args if instantiating_a_class? puts "Side effect happening..." else puts "Don't do anything..." end end end 

为什么要向self发送消息会导致initialize方法再次被调用? 这在任何地方记录? 这是预期的吗?

这就是basename的实现方式,它返回一个新实例:

 /* * Returns the last component of the path. * * See File.basename. */ static VALUE path_basename(int argc, VALUE *argv, VALUE self) { VALUE str = get_strpath(self); VALUE fext; if (rb_scan_args(argc, argv, "01", &fext) == 0) str = rb_funcall(rb_cFile, rb_intern("basename"), 1, str); else str = rb_funcall(rb_cFile, rb_intern("basename"), 2, str, fext); return rb_class_new_instance(1, &str, rb_obj_class(self)); } 

最后一行相当于调用new

您可以轻松validation:

 class Foo < Pathname def initialize(path) puts "initialize(#{path.inspect})" super end end foo = Foo.new('foo/bar/baz') # prints initialize("foo/bar/baz") #=> # foo.basename # prints initialize("baz") #=> # 

正如你所说,它与basename方法有关。 从文档的源代码中可以看出, basename实例化了该类的另一个对象。

Pathname实例方法通常返回Pathname实例。 为此,他们需要在当前类上调用initialize

如果你看一下basename的源代码:

  return rb_class_new_instance(1, &str, rb_obj_class(self)); 

如果它不是FooBar类的所需function,则可以停止inheritancePathname ,并定义@pathname实例变量。

最后,你可能不希望像昨天提议的那样在initialize自动创建目录:

获取file.txt的基本名称可以创建file目录。

第4步让我完全惊讶。 为什么要向self发送消息会导致initialize方法再次被调用? 这在任何地方记录? 这是预期的吗?

嗯,是。 允许方法调用其他方法。 这几乎是方法的重点。 basename返回一个新的Pathname对象。 那么,您认为它如何构建这个新的Pathname对象? 当然,它调用self.class::new (实际上是Class#new ),它又调用Pathname#initialize

这就是Rubinius实现Ruby标准库时Pathname#basename #basename的实现 :

 def basename(*args) self.class.new(File.basename(@path, *args)) end 

Class#new的实现大致如下:

 class Class def new(*args, &block) # allocate a new empty object from the ObjectSpace obj = allocate # initialize it (must use send because initialize is private) obj.send(:initialize, *args, &block) # return object that was initialized obj end end