簡體   English   中英

在擴展自身的模塊中的實例方法中調用單例方法

[英]Calling a singleton method within an instance method in a module that extends itself

我自己擴展了Kernel ,並且在實例方法Kernel#abort的定義中,我調用了單例方法Kernel.abort

module Kernel
  extend self

  def abort
    puts "Press ENTER to exit..."
    gets
    Kernel.abort
  end
end

abort

當我調用Kernel#abort ,似乎方法定義中的Kernel.abort調用引用了原始Kernel#abort Kernel.abort (擴展為Kernel.abort )。

Ruby如何知道當我編寫Kernel.abort ,我的意思是原始的abort方法,而不是我剛創建的方法? 我將如何遞歸調用剛剛創建的新abort方法?

Kernel.abort的定義是首先定義一個實例方法Kernel#abort ,然后使它成為一個帶有module_function的單例方法。 這在Rubinius中肯定是這種情況 ;我在MRI源中找不到它,但見下文。) module_function 復制了該方法。 重新定義abort ,重新定義實例方法,但不重新定義單例副本。

Object包括Kernel ,所以當你說abort你得到了你重新定義的實例方法,但是當你說Kernel.abort你會得到你沒有重新定義的單例方法。

如果您真的想在abort使用遞歸,或者只是為了證明這種解釋是正確的,請在重新定義方法后調用module_function :abort 單例方法將更新為與實例方法相同,並且兩種方法都將遞歸。

請注意,您不需要extend self來重新定義abort的實例版本。 由於Kernel已包含在Object ,因此您只需要為所有對象重新定義實例方法以查看重新定義的版本。 另一方面,如果Kernel首先使用extend self來暴露#abort ,我們可以重新定義它而不會出現任何復雜情況。

下面演示了用戶定義的純Ruby方法缺少遞歸,即module_function是負責人而本機方法不是:

$ cat foo.rb
module Foo
  def bar
    puts "old version"
  end
  module_function :bar
end

module Foo
  def bar
    puts "new version"
    Foo.bar
  end
end

Object.include Foo
bar
$ ruby foo.rb
new version
old version

你應該做這樣的事情:

module Kernel
    class << self
        alias :real_abort :abort
        def abort
            puts "press enter"
            gets
            puts "invoking real abort"
            real_abort
        end
    end
end

IRB調用原始中止而不是您定義的abort的原因是因為IRB repl有一個中止它的C語言變體的中止方法。

你可以通過在pry上做show-source來看到它

[1] pry(main)> show-source abort

From: process.c (C Method):
Owner: Kernel
Visibility: private
Number of lines: 22

VALUE
rb_f_abort(int argc, const VALUE *argv)
{
    rb_check_arity(argc, 0, 1);
    if (argc == 0) {
    if (!NIL_P(GET_THREAD()->errinfo)) {
        ruby_error_print();
    }
    rb_exit(EXIT_FAILURE);
    }
    else {
    VALUE args[2];

    args[1] = args[0] = argv[0];
    StringValue(args[0]);
    rb_io_puts(1, args, rb_stderr);
    args[0] = INT2NUM(EXIT_FAILURE);
    rb_exc_raise(rb_class_new_instance(2, args, rb_eSystemExit));
    }

    UNREACHABLE;
}

在您提到的代碼中,正在執行的中止不是Kernel.abort,而是從命令行調用它時映射到IRB的C語言中止

如果你想要它被屏蔽你可以做這樣的事情,以便IRB repl中的abort將執行你重新定義的abort

def abort; Kernel.abort; end

然后,如果你運行abort ,它將調用你重新定義的內核的中止單例方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM