【问题标题】:Which Ruby classes support .clone?哪些 Ruby 类支持 .clone?
【发布时间】:2010-11-27 23:05:35
【问题描述】:

Ruby 在 Object 中定义了#clone。 令我惊讶的是,某些类在调用它时会引发异常。 我发现 NilClassTrueClassFalseClassFixnum 有这种行为。

1) 是否存在不允许 #clone 的类的完整列表(至少是核心类)? 或者有没有办法检测特定类是否支持#clone

2) 42.clone 有什么问题?

【问题讨论】:

  • 我真的很想知道你如何测试一个类是否可以自己克隆。似乎如果一个类不想让自己被克隆,那么它应该将它从 Object 继承的克隆方法设为私有,这样你就可以只在 public_methods 下测试它的存在。对我来说似乎是常识。

标签: ruby clone


【解决方案1】:

Rails 似乎使用“可复制的?()”方法扩展了您提到的类。

http://api.rubyonrails.org/files/activesupport/lib/active_support/core_ext/object/duplicable_rb.html

【讨论】:

  • duplicatable?() 也在 ActiveSupport gem 中定义
【解决方案2】:

我对YARV的源代码做了git grep "can't clone",得到了

lib/singleton.rb:    raise TypeError, "can't clone instance of singleton #{self.class}"
object.c:        rb_raise(rb_eTypeError, "can't clone %s", rb_obj_classname(obj));
test/test_singleton.rb:    expected = "can't clone instance of singleton TestSingleton::SingletonTest"

第一行和第三行表示您不能克隆单例。

第二行引用rb_special_const_p(obj)。但这超出了我的理解范围。

【讨论】:

    【解决方案3】:

    我仍然不知道如何正确测试可克隆性,但这是使用错误捕获来测试可克隆性的一种非常笨拙、邪恶的方法:

    def clonable?(value)
      begin
        clone = value.clone
        true
      rescue
        false
      end
    end
    

    以下是克隆甚至无法克隆的方法。至少对于我已经厌倦的极少数课程而言。

    def super_mega_clone(value)
      eval(value.inspect)
    end
    

    以下是一些示例测试:

    b = :b
    puts "clonable? #{clonable? b}"
    
    b = proc { b == "b" }
    puts "clonable? #{clonable? b}"
    
    b = [:a, :b, :c]
    c = super_mega_clone(b)
    
    puts "c: #{c.object_id}"
    puts "b: #{b.object_id}"
    puts "b == c => #{b == c}"
    b.each_with_index do |value, index|
      puts "[#{index}] b: #{b[index].object_id} c: #{c[index].object_id}"
    end
    b[0] = :z
    
    puts "b == c => #{b == c}"
    b.each_with_index do |value, index|
      puts "[#{index}] b: #{b[index].object_id} c: #{c[index].object_id}"
    end
    
    b = :a
    c = super_mega_clone(b)
    puts "b: #{b.object_id} c: #{c.object_id}"
    
    > clonable? false
    > clonable? true
    > c: 2153757040
    > b: 2153757480
    > b == c => true
    > [0] b: 255528 c: 255528
    > [1] b: 255688 c: 255688
    > [2] b: 374568 c: 374568
    > b == c => false
    > [0] b: 1023528 c: 255528
    > [1] b: 255688 c: 255688
    > [2] b: 374568 c: 374568
    > b: 255528 c: 255528
    

    【讨论】:

      【解决方案4】:

      Fixnum 是一个特殊的类,由语言给予特殊处理。从你的程序启动的那一刻起,类可以表示的每个数字都有一个 Fixnum,并且它们被赋予了一个不占用任何额外空间的特殊表示——这样,基本的数学运算就不会分配和释放记忆像疯了似的。因此,42 不能超过一个。

      对于其他人来说,他们都有一个共同点:他们都是单身人士。根据定义,单例类只有一个实例,因此尝试克隆它是错误的。

      【讨论】:

      • “因此,42 不能超过一个。”。为什么需要?这是完美的。
      【解决方案5】:

      我认为没有正式的列表,至少除非你算上阅读源代码。原因 2) 不起作用是因为对 Fixnums 进行了优化。它们作为实际值在内部存储/传递(true、false 和 nil 也是如此),而不是作为指针。幼稚的解决方案是让42.clone 返回相同的42,但不变的obj.clone.object_id != obj.object_id 将不再成立,42.clone 实际上不会被克隆。

      【讨论】:

      • obj.clone.object_id != obj.object_id 为真和obj.clone.object_id == obj.object_id 并不总是为真是不同的。前者不成立,不代表后者不成立。
      【解决方案6】:

      你不能克隆不可变的类。 IE。您只能拥有一个对象 42 的实例(作为 Fixnum),但可以拥有多个“42”的实例(因为字符串是可变的)。您也不能克隆符号,因为它们类似于不可变字符串。

      您可以使用 object_id 方法在 IRB 中检查。 (重复调用后,symbols 和 fixnums 会给你相同的 object_id)

      【讨论】:

      • Mutability 与它无关(实际上,您可以将状态添加到 Fixnum)。
      • Fixnum 的默认行为真的很奇怪,特别是考虑到它确实具有克隆方法 d.class.method_defined?(:clone) == true
      猜你喜欢
      • 1970-01-01
      • 2016-09-20
      • 2018-12-14
      • 2011-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多