公共课程方法的位置'新的' '班级'

时间:2017-07-04 06:08:43

标签: ruby

Ruby的Class类列出了两个名为' new'的方法:

但是当我这样做时:

Class.methods(false)
#=> []

应该列出单例方法(我假设是什么类方法),我得到一个空数组。为什么会这样? Class::new定义在哪里?

3 个答案:

答案 0 :(得分:3)

文档中显示为::new的方法通常为#initialize,例如Range::new

  

new(begin,end,exclude_end = false)→rng

     

使用给定的beginend构造范围。如果省略exclude_end参数或false,则rng将包含结束对象;否则,它将被排除在外。

这是因为您通过以下方式创建实例:

r = Range.new(0, 2)       #=> 0..2

而不是:

r = Range.allocate        #=> nil..nil
r.send(:initialize, 0, 2) #=> nil
r                         #=> 0..2

这正是::new的作用 - 它通过allocate创建一个新实例,发送它initialize(传递参数)并返回实例。

实际new方法继承自Class(因为RangeClass的实例) - Class#new

  

new(args,...)→obj

     

调用allocate创建类的新对象,然后调用该对象的initialize方法,并将其传递给 args 。这是在使用.new。

构造对象时最终调用的方法

就像allocateinheritedsuperclass(以及来自Class'祖先的实例方法,例如Module):

Range.method(:new)
#=> #<Method: Class#new>

Range.method(:allocate)
#=> #<Method: Class#allocate>

Range.method(:ancestors)
#=> #<Method: Class(Module)#ancestors>

所以,如果你致电Class.new

my_string_class = Class.new(String)
#=> #<Class:0x007fdf5485b200>

你只需调用Class#new,它(再次)相当于:

my_string_class = Class.allocate
my_string_class.send(:initialize, String)
my_string_class
#=> #<Class:0x007fdf5484beb8>

一个值得注意的例外是Struct,它实际上提供了自己的new类方法:

Struct.method(:new)
#=> #<Method: Struct.new>

与其他类不同,Struct::new不返回Struct的实例,而是返回Class的实例(Struct的子类)。

答案 1 :(得分:2)

tl; dr summary

  

为什么会这样?

因为它不是单身方法。

  

Class::new定义在哪里?

不是。 调用 Class.new只是调用Class#new(因为Class是其自身的实例)。 Foo::new文档实际上是任何Foo#initialize的{​​{1}}文档,包括Foo本身。< / p>

如果你想了解一些关于Ruby的事情,那么自己问问她是个好主意:

Class

Object#method方法返回表示方法的Method对象。 (方法不是Ruby本身的对象,但是你可以得到一个代表方法的反射代理对象。)

您可以使用Method#owner方法询问new_method = Class.method(:new) #=> #<Method: Class#new (defined in Class at core/alpha.rb:90)> 定义的位置:

Method

如您所见,newClass中定义,而不在new_method.owner #=> Class 的单身类中定义。

您还可以使用Method#source_location方法向Class询问有关其Ruby源代码的位置:

Method

这告诉我们new_method.source_location #=> ['core/alpha.rb', 90] 是在文件core/alpha.rb on line 90中定义的:

Class#new

出于性能原因,该方法部分以字节码实现,但基本上只是:

def new(*args)
  obj = allocate()

  Rubinius.asm(args, obj) do |args, obj|
    run obj
    run args
    push_block
    send_with_splat :initialize, 0, true
    # no pop here, as .asm blocks imply a pop as they're not
    # allowed to leak a stack value
  end

  obj
end

现在,您可能会问自己:为什么在RDoc文档中有Class::new的条目,如果该方法不存在?好吧,RDoc知道class Class def new(*args, &block) obj = allocate obj.__send__(:initialize, *args, &block) # because initialize is private #obj.initialize(*args, &block) obj end end 之间的关系,这是您定义的方法,但通常不会直接调用,#initialize这是您调用的方法,但通常不会...如果存在,它会将Class#new记录为#initialize

所以,我们真正想看的是::new

Class#initialize

This is the source

initialize_method = Class.method(:initialize)
#=> #<Method: Class#initialize (defined in Class at core/class.rb:15)>

initialize_method.owner
#=> Class

initialize_method.source_location
#=> ['core/class.rb', 15]

def initialize(sclass=Object, name=nil, under=nil) raise TypeError, "already initialized class" if @instance_type raise TypeError, "can't make subclass of Class" if Class.equal?(sclass) set_superclass sclass # Things (rails) depend on the fact that a normal class is in the constant # table and have a name BEFORE inherited is run. under.const_set name, self if under if sclass Rubinius.privately do sclass.inherited self end end super() end private :initialize 基本上做了三件事:

  • 设置超类
  • 可选择将类分配给常量以赋予其名称
  • 调用超类的Class#inherited钩子方法

如果你想知道一开始就神奇地存在的一些核心类之间的关系,你可以看看一些Ruby执行引擎的初始化代码,例如:

注意:根据您使用的Ruby实现,显然这些方法的定义位置以及定义方式可能会有所不同。

答案 2 :(得分:1)

new被定义为Class类的实例方法,而不是单例方法:

Class.instance_method :new     # => #<UnboundMethod: Class#new>

需要注意的是:Class(对象)本身也是Class(类)的实例。

Class.instance_of? Class       # => true