类 BasicObject

BasicObject 是 Ruby 中所有类的父类。它是一个显式的空类。

BasicObject 可用于创建独立于 Ruby 对象层次结构的对象层次结构、代理对象(如 Delegator 类)或其他需要避免 Ruby 方法和类命名空间污染的情况。

为了避免污染其他用户使用的 BasicObject,应该创建 BasicObject 的适当命名的子类,而不是直接修改 BasicObject。

class MyObjectSystem < BasicObject
end

BasicObject 不包含 Kernel(用于 puts 等方法),并且 BasicObject 位于标准库的命名空间之外,因此如果没有使用完整类路径,则无法找到常见类。

可以使用多种策略为 BasicObject 的子类提供标准库的有用部分。子类可以 include Kernel 来获取 putsexit 等。可以创建自定义的类似 Kernel 的模块并包含它,或者可以通过 method_missing 使用委托。

class MyObjectSystem < BasicObject
  DELEGATE = [:puts, :p]

  def method_missing(name, *args, &block)
    return super unless DELEGATE.include? name
    ::Kernel.send(name, *args, &block)
  end

  def respond_to_missing?(name, include_private = false)
    DELEGATE.include?(name) or super
  end
end

可以通过在 BasicObject 子类中从根目录引用所需的常量(如 ::File::Enumerator)来获取 Ruby 标准库中的类和模块。与 method_missing 类似,const_missing 可用于将常量查找委托给 Object

class MyObjectSystem < BasicObject
  def self.const_missing(name)
    ::Object.const_get(name)
  end
end

这里有什么

这些是为 BasicObject 定义的方法

  • ::new: 返回一个新的 BasicObject 实例。

  • #!: 返回 self 的布尔否定值:truefalse

  • #!=: 返回 self 和给定对象是否相等。

  • ==: 返回 self 和给定对象是否等效。

  • #__id__: 返回 self 的整数对象标识符。

  • #__send__: 调用由给定符号标识的方法。

  • equal?: 返回 self 和给定对象是否为同一个对象。

  • instance_eval: 在self的上下文中评估给定的字符串或代码块。

  • instance_exec: 在self的上下文中执行给定的代码块,并传递给定的参数。

公共类方法

new 点击切换源代码

返回一个新的 BasicObject

#define rb_obj_initialize rb_obj_dummy0

公共实例方法

!obj → true 或 false 点击切换源代码

布尔取反。

VALUE
rb_obj_not(VALUE obj)
{
    return RBOOL(!RTEST(obj));
}
obj != other → true 或 false 点击切换源代码

如果两个对象不相等,则返回 true,否则返回 false。

VALUE
rb_obj_not_equal(VALUE obj1, VALUE obj2)
{
    VALUE result = rb_funcall(obj1, id_eq, 1, obj2);
    return rb_obj_not(result);
}
obj == other → true 或 false 点击切换源代码
eql?(other) → true 或 false

相等性 - 在 Object 层面上,== 仅当 objother 是同一个对象时才返回 true。通常,此方法在子类中被重写以提供特定于类的含义。

== 不同,equal? 方法不应该被子类重写,因为它用于确定对象标识(即,a.equal?(b) 当且仅当 ab 是同一个对象时)。

obj = "a"
other = obj.dup

obj == other      #=> true
obj.equal? other  #=> false
obj.equal? obj    #=> true

eql? 方法如果 objother 指向同一个哈希键,则返回 true。这被 Hash 用于测试成员的相等性。对于任何一对 eql? 返回 true 的对象,两个对象的哈希值必须相等。因此,任何重写 eql? 的子类也应该相应地重写 hash。

对于类 Object 的对象,eql? 等同于 ==。子类通常通过将 eql? 设为其重写的 == 方法的别名来延续这一传统,但也有一些例外。例如,Numeric 类型在 == 中执行类型转换,但在 eql? 中不执行,因此

1 == 1.0     #=> true
1.eql? 1.0   #=> false
VALUE
rb_obj_equal(VALUE obj1, VALUE obj2)
{
    return RBOOL(obj1 == obj2);
}
也称为:equal?
__id__ → 整数 点击切换源代码
object_id → 整数

返回obj的整数标识符。

对于给定的对象,对object_id的所有调用都将返回相同的数字,并且没有两个活动对象共享相同的id。

注意:为了优化,某些内置类对象会被重复使用。这种情况适用于立即值和冻结字符串字面量。

BasicObject 实现 +__id__+,Kernel 实现 object_id

立即值不是通过引用传递,而是通过值传递:niltruefalse、Fixnums、Symbols 和一些 Floats。

Object.new.object_id  == Object.new.object_id  # => false
(21 * 2).object_id    == (21 * 2).object_id    # => true
"hello".object_id     == "hello".object_id     # => false
"hi".freeze.object_id == "hi".freeze.object_id # => true
VALUE
rb_obj_id(VALUE obj)
{
    /*
     *                32-bit VALUE space
     *          MSB ------------------------ LSB
     *  false   00000000000000000000000000000000
     *  true    00000000000000000000000000000010
     *  nil     00000000000000000000000000000100
     *  undef   00000000000000000000000000000110
     *  symbol  ssssssssssssssssssssssss00001110
     *  object  oooooooooooooooooooooooooooooo00        = 0 (mod sizeof(RVALUE))
     *  fixnum  fffffffffffffffffffffffffffffff1
     *
     *                    object_id space
     *                                       LSB
     *  false   00000000000000000000000000000000
     *  true    00000000000000000000000000000010
     *  nil     00000000000000000000000000000100
     *  undef   00000000000000000000000000000110
     *  symbol   000SSSSSSSSSSSSSSSSSSSSSSSSSSS0        S...S % A = 4 (S...S = s...s * A + 4)
     *  object   oooooooooooooooooooooooooooooo0        o...o % A = 0
     *  fixnum  fffffffffffffffffffffffffffffff1        bignum if required
     *
     *  where A = sizeof(RVALUE)/4
     *
     *  sizeof(RVALUE) is
     *  20 if 32-bit, double is 4-byte aligned
     *  24 if 32-bit, double is 8-byte aligned
     *  40 if 64-bit
     */

    return rb_find_object_id(obj, cached_object_id);
}
send(symbol [, args...]) → obj click to toggle source
__send__(symbol [, args...]) → obj
send(string [, args...]) → obj
__send__(string [, args...]) → obj

调用由symbol标识的方法,并将指定的任何参数传递给它。当方法由字符串标识时,字符串将被转换为符号。

BasicObject 实现 +__send__+,Kernel 实现 send。当obj具有与Socket相同的名称时,__send__send更安全。另请参阅 public_send

class Klass
  def hello(*args)
    "Hello " + args.join(' ')
  end
end
k = Klass.new
k.send :hello, "gentle", "readers"   #=> "Hello gentle readers"
VALUE
rb_f_send(int argc, VALUE *argv, VALUE recv)
{
    return send_internal_kw(argc, argv, recv, CALL_FCALL);
}
equal?(other) → true 或 false
eql?(other) → true 或 false

相等性 - 在 Object 层面上,== 仅当 objother 是同一个对象时才返回 true。通常,此方法在子类中被重写以提供特定于类的含义。

== 不同,equal? 方法不应该被子类重写,因为它用于确定对象标识(即,a.equal?(b) 当且仅当 ab 是同一个对象时)。

obj = "a"
other = obj.dup

obj == other      #=> true
obj.equal? other  #=> false
obj.equal? obj    #=> true

eql? 方法如果 objother 指向同一个哈希键,则返回 true。这被 Hash 用于测试成员的相等性。对于任何一对 eql? 返回 true 的对象,两个对象的哈希值必须相等。因此,任何重写 eql? 的子类也应该相应地重写 hash。

对于类 Object 的对象,eql? 等同于 ==。子类通常通过将 eql? 设为其重写的 == 方法的别名来延续这一传统,但也有一些例外。例如,Numeric 类型在 == 中执行类型转换,但在 eql? 中不执行,因此

1 == 1.0     #=> true
1.eql? 1.0   #=> false
别名:==
instance_eval(string [, filename [, lineno]] ) → obj click to toggle source
instance_eval {|obj| block } → obj

在接收者(obj)的上下文中评估包含 Ruby 源代码的字符串或给定的块。为了设置上下文,变量self在代码执行期间被设置为obj,使代码能够访问obj的实例变量和私有方法。

instance_eval给定一个块时,obj也会作为块的唯一参数传递。

instance_eval给定一个String时,可选的第二个和第三个参数提供一个文件名和起始行号,这些行号在报告编译错误时使用。

class KlassWithSecret
  def initialize
    @secret = 99
  end
  private
  def the_secret
    "Ssssh! The secret is #{@secret}."
  end
end
k = KlassWithSecret.new
k.instance_eval { @secret }          #=> 99
k.instance_eval { the_secret }       #=> "Ssssh! The secret is 99."
k.instance_eval {|obj| obj == self } #=> true
static VALUE
rb_obj_instance_eval_internal(int argc, const VALUE *argv, VALUE self)
{
    return specific_eval(argc, argv, self, TRUE, RB_PASS_CALLED_KEYWORDS);
}
instance_exec(arg...) {|var...| block } → obj click to toggle source

在接收者(obj)的上下文中执行给定的块。为了设置上下文,变量self在代码执行期间被设置为obj,使代码能够访问obj的实例变量。参数作为块参数传递。

class KlassWithSecret
  def initialize
    @secret = 99
  end
end
k = KlassWithSecret.new
k.instance_exec(5) {|x| @secret+x }   #=> 104
static VALUE
rb_obj_instance_exec_internal(int argc, const VALUE *argv, VALUE self)
{
    return yield_under(self, TRUE, argc, argv, RB_PASS_CALLED_KEYWORDS);
}

私有实例方法

method_missing(symbol [, *args] ) → result click to toggle source

obj收到无法处理的消息时,由 Ruby 调用。symbol 是调用的方法的符号,args 是传递给它的任何参数。默认情况下,解释器在调用此方法时会引发错误。但是,可以覆盖该方法以提供更动态的行为。如果决定不处理特定方法,则应调用super,以便祖先可以接管丢失的方法。下面的示例创建了一个类Roman,它响应以罗马数字命名的方法,并返回相应的整数值。

class Roman
  def roman_to_int(str)
    # ...
  end

  def method_missing(symbol, *args)
    str = symbol.id2name
    begin
      roman_to_int(str)
    rescue
      super(symbol, *args)
    end
  end
end

r = Roman.new
r.iv      #=> 4
r.xxiii   #=> 23
r.mm      #=> 2000
r.foo     #=> NoMethodError
static VALUE
rb_method_missing(int argc, const VALUE *argv, VALUE obj)
{
    rb_execution_context_t *ec = GET_EC();
    raise_method_missing(ec, argc, argv, obj, ec->method_missing_reason);
    UNREACHABLE_RETURN(Qnil);
}
singleton_method_added(symbol) 点击切换源代码

每当向接收方添加单例方法时,就会作为回调调用。

module Chatty
  def Chatty.singleton_method_added(id)
    puts "Adding #{id.id2name}"
  end
  def self.one()     end
  def two()          end
  def Chatty.three() end
end

产生

Adding singleton_method_added
Adding one
Adding three
#define rb_obj_singleton_method_added rb_obj_dummy1
singleton_method_removed(symbol) 点击切换源代码

每当从接收方移除单例方法时,就会作为回调调用。

module Chatty
  def Chatty.singleton_method_removed(id)
    puts "Removing #{id.id2name}"
  end
  def self.one()     end
  def two()          end
  def Chatty.three() end
  class << self
    remove_method :three
    remove_method :one
  end
end

产生

Removing three
Removing one
#define rb_obj_singleton_method_removed rb_obj_dummy1
singleton_method_undefined(symbol) 点击切换源代码

每当在接收方中未定义单例方法时,就会作为回调调用。

module Chatty
  def Chatty.singleton_method_undefined(id)
    puts "Undefining #{id.id2name}"
  end
  def Chatty.one()   end
  class << self
     undef_method(:one)
  end
end

产生

Undefining one
#define rb_obj_singleton_method_undefined rb_obj_dummy1